@libraz/libcantus 1.3.0 → 1.4.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 (130) hide show
  1. package/NOTICE +63 -0
  2. package/README.md +3 -1
  3. package/README_ja.md +13 -11
  4. package/dist/analyze/index.cjs +7 -7
  5. package/dist/analyze/index.cjs.map +1 -1
  6. package/dist/analyze/index.d.cts +14 -12
  7. package/dist/analyze/index.d.ts +14 -12
  8. package/dist/analyze/index.js +6 -6
  9. package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
  10. package/dist/chunk-2G5MCGYU.js.map +1 -0
  11. package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
  12. package/dist/chunk-3FDBMIPR.cjs.map +1 -0
  13. package/dist/{chunk-YFA7X2F6.cjs → chunk-3MDOEFIQ.cjs} +411 -327
  14. package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
  15. package/dist/{chunk-IPEF6NWB.js → chunk-5MWRBYAT.js} +278 -154
  16. package/dist/chunk-5MWRBYAT.js.map +1 -0
  17. package/dist/{chunk-B2PW6EHN.cjs → chunk-6TTBEON3.cjs} +1253 -889
  18. package/dist/chunk-6TTBEON3.cjs.map +1 -0
  19. package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
  20. package/dist/chunk-7NQ6AL3H.js.map +1 -0
  21. package/dist/{chunk-PWC4CRWT.cjs → chunk-E3QO3UET.cjs} +31 -23
  22. package/dist/chunk-E3QO3UET.cjs.map +1 -0
  23. package/dist/{chunk-GNVN4PPJ.js → chunk-FJFULJZE.js} +531 -213
  24. package/dist/chunk-FJFULJZE.js.map +1 -0
  25. package/dist/{chunk-CPMB7P75.js → chunk-FK3DGAIZ.js} +608 -244
  26. package/dist/chunk-FK3DGAIZ.js.map +1 -0
  27. package/dist/chunk-GEDTJHAC.cjs.map +1 -1
  28. package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
  29. package/dist/chunk-H7FVMBUC.js.map +1 -0
  30. package/dist/{chunk-4R2LWV7E.cjs → chunk-HYSVSFNI.cjs} +664 -540
  31. package/dist/chunk-HYSVSFNI.cjs.map +1 -0
  32. package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
  33. package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
  34. package/dist/{chunk-IFZDWIKM.cjs → chunk-LQZQDBCD.cjs} +1048 -730
  35. package/dist/chunk-LQZQDBCD.cjs.map +1 -0
  36. package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
  37. package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
  38. package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
  39. package/dist/chunk-S3QELJUE.cjs.map +1 -0
  40. package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
  41. package/dist/chunk-TXFWSU5D.js.map +1 -0
  42. package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
  43. package/dist/chunk-U4EQTDGN.cjs.map +1 -0
  44. package/dist/chunk-UGLFDG7L.cjs.map +1 -1
  45. package/dist/{chunk-MLBPXA4Z.js → chunk-UTE5A36R.js} +198 -114
  46. package/dist/chunk-UTE5A36R.js.map +1 -0
  47. package/dist/{chunk-ZBUROTJQ.js → chunk-UZKQISRF.js} +17 -9
  48. package/dist/chunk-UZKQISRF.js.map +1 -0
  49. package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
  50. package/dist/chunk-VL5NXVCN.js.map +1 -0
  51. package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
  52. package/dist/chunk-WOMCXN2W.cjs.map +1 -0
  53. package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
  54. package/dist/chunk-YDGMWH43.cjs.map +1 -0
  55. package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
  56. package/dist/chunk-YYVXRV3V.js.map +1 -0
  57. package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
  58. package/dist/chunk-ZLNSH4UL.js.map +1 -0
  59. package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
  60. package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
  61. package/dist/core/index.cjs +5 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +9 -8
  64. package/dist/core/index.d.ts +9 -8
  65. package/dist/core/index.js +4 -4
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.cjs.map +1 -1
  68. package/dist/generate/index.d.cts +28 -18
  69. package/dist/generate/index.d.ts +28 -18
  70. package/dist/generate/index.js +7 -7
  71. package/dist/{index-BMweUlm5.d.cts → index-5VJgKz_Q.d.cts} +6 -2
  72. package/dist/{index-BHIUyjhZ.d.ts → index-BLuDesVG.d.ts} +20 -13
  73. package/dist/{index-B9QLziDe.d.cts → index-BV6APVa6.d.cts} +20 -13
  74. package/dist/{index-CL4b4gUQ.d.ts → index-Bf0Sv8a9.d.ts} +6 -2
  75. package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
  76. package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
  77. package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
  78. package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
  79. package/dist/{index-CY-3NVfy.d.cts → index-DVInzCDS.d.cts} +53 -29
  80. package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
  81. package/dist/{index-zaZdmnU4.d.ts → index-jgeug9EA.d.ts} +53 -29
  82. package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
  83. package/dist/index.cjs +13 -13
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +11 -11
  86. package/dist/index.d.ts +11 -11
  87. package/dist/index.js +12 -12
  88. package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
  89. package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
  90. package/dist/model/index.cjs +12 -12
  91. package/dist/model/index.cjs.map +1 -1
  92. package/dist/model/index.d.cts +63 -48
  93. package/dist/model/index.d.ts +63 -48
  94. package/dist/model/index.js +11 -11
  95. package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
  96. package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
  97. package/dist/{profile-DLdlIpS9.d.ts → profile-B4WqcM4H.d.ts} +25 -17
  98. package/dist/{profile-CQMEqdH9.d.cts → profile-BJd1XkPB.d.cts} +25 -17
  99. package/dist/theory/index.cjs +5 -5
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +18 -14
  102. package/dist/theory/index.d.ts +18 -14
  103. package/dist/theory/index.js +4 -4
  104. package/dist/{types-D-XgxGvp.d.cts → types-7mGFwvMm.d.ts} +45 -22
  105. package/dist/{types-Dh7C064C.d.ts → types-_ofpb2be.d.cts} +45 -22
  106. package/package.json +13 -13
  107. package/dist/chunk-35NBV2FT.cjs.map +0 -1
  108. package/dist/chunk-3R3QVKY5.cjs.map +0 -1
  109. package/dist/chunk-4R2LWV7E.cjs.map +0 -1
  110. package/dist/chunk-76ZBTRG2.cjs.map +0 -1
  111. package/dist/chunk-7KG3RTYS.js.map +0 -1
  112. package/dist/chunk-AGOVW7U6.js.map +0 -1
  113. package/dist/chunk-B2PW6EHN.cjs.map +0 -1
  114. package/dist/chunk-CGBADIO5.cjs.map +0 -1
  115. package/dist/chunk-CPMB7P75.js.map +0 -1
  116. package/dist/chunk-GNVN4PPJ.js.map +0 -1
  117. package/dist/chunk-IFZDWIKM.cjs.map +0 -1
  118. package/dist/chunk-IPEF6NWB.js.map +0 -1
  119. package/dist/chunk-M5RYMAFI.js.map +0 -1
  120. package/dist/chunk-MLBPXA4Z.js.map +0 -1
  121. package/dist/chunk-NB25P3EH.js.map +0 -1
  122. package/dist/chunk-NM7UAMC3.cjs.map +0 -1
  123. package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
  124. package/dist/chunk-PUKJP2Z5.js.map +0 -1
  125. package/dist/chunk-PWC4CRWT.cjs.map +0 -1
  126. package/dist/chunk-RFIBF2RR.js.map +0 -1
  127. package/dist/chunk-S5BKZZGM.cjs.map +0 -1
  128. package/dist/chunk-UMWZ4ZET.js.map +0 -1
  129. package/dist/chunk-YFA7X2F6.cjs.map +0 -1
  130. package/dist/chunk-ZBUROTJQ.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunk35NBV2FTcjs = require('./chunk-35NBV2FT.cjs');
4
3
 
4
+ var _chunkKMYGRFZ6cjs = require('./chunk-KMYGRFZ6.cjs');
5
5
 
6
6
 
7
7
 
@@ -48,11 +48,11 @@ var _chunk35NBV2FTcjs = require('./chunk-35NBV2FT.cjs');
48
48
 
49
49
 
50
50
 
51
- var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
52
51
 
53
52
 
54
53
 
55
54
 
55
+ var _chunkYDGMWH43cjs = require('./chunk-YDGMWH43.cjs');
56
56
 
57
57
 
58
58
 
@@ -90,18 +90,24 @@ var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
90
90
 
91
91
 
92
92
 
93
- var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
93
+
94
+
95
+
96
+
97
+
98
+
99
+ var _chunkS3QELJUEcjs = require('./chunk-S3QELJUE.cjs');
94
100
 
95
101
  // src/analyze/detect/modes.ts
96
102
  var MODAL_CANDIDATES = Object.freeze([
97
- { scaleName: "dorian", mode: "minor", mask: _chunk3R3QVKY5cjs.DORIAN_MASK, substitutions: [[8, 9]] },
98
- { scaleName: "phrygian", mode: "minor", mask: _chunk3R3QVKY5cjs.PHRYGIAN_MASK, substitutions: [[2, 1]] },
99
- { scaleName: "lydian", mode: "major", mask: _chunk3R3QVKY5cjs.LYDIAN_MASK, substitutions: [[5, 6]] },
100
- { scaleName: "mixolydian", mode: "major", mask: _chunk3R3QVKY5cjs.MIXOLYDIAN_MASK, substitutions: [[11, 10]] },
103
+ { scaleName: "dorian", mode: "minor", mask: _chunkYDGMWH43cjs.DORIAN_MASK, substitutions: [[8, 9]] },
104
+ { scaleName: "phrygian", mode: "minor", mask: _chunkYDGMWH43cjs.PHRYGIAN_MASK, substitutions: [[2, 1]] },
105
+ { scaleName: "lydian", mode: "major", mask: _chunkYDGMWH43cjs.LYDIAN_MASK, substitutions: [[5, 6]] },
106
+ { scaleName: "mixolydian", mode: "major", mask: _chunkYDGMWH43cjs.MIXOLYDIAN_MASK, substitutions: [[11, 10]] },
101
107
  {
102
108
  scaleName: "locrian",
103
109
  mode: "minor",
104
- mask: _chunk3R3QVKY5cjs.LOCRIAN_MASK,
110
+ mask: _chunkYDGMWH43cjs.LOCRIAN_MASK,
105
111
  substitutions: [
106
112
  [2, 1],
107
113
  [7, 6]
@@ -128,12 +134,12 @@ function resolveModalCandidates(modes) {
128
134
  return MODAL_CANDIDATES;
129
135
  }
130
136
  if (!Array.isArray(modes)) {
131
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("modes must be a boolean or an array of mode names");
137
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)("modes must be a boolean or an array of mode names");
132
138
  }
133
139
  const requested = new Set(modes);
134
140
  for (const name of requested) {
135
141
  if (!MODAL_SCALE_NAMES.some((known) => known === name)) {
136
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
142
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
137
143
  `unknown mode '${String(name)}'; expected one of ${MODAL_SCALE_NAMES.join(", ")}`
138
144
  );
139
145
  }
@@ -144,7 +150,7 @@ function resolveModalCandidates(modes) {
144
150
  // src/analyze/adjacency.ts
145
151
  var HUMANIZE_ADJACENCY = 0.05;
146
152
  function sameInstant(beat, other) {
147
- return Math.abs(beat - other) <= _chunkS5BKZZGMcjs.BEAT_EPS;
153
+ return Math.abs(beat - other) <= _chunkS3QELJUEcjs.BEAT_EPS;
148
154
  }
149
155
  function adjacent(prevEnd, nextStart) {
150
156
  return Math.abs(nextStart - prevEnd) <= HUMANIZE_ADJACENCY;
@@ -158,7 +164,7 @@ var MAX_VELOCITY = 127;
158
164
  var ACCENT_DIVISOR = 3;
159
165
  function noteWeight(note, beats, meter) {
160
166
  const velocityFactor = note.velocity !== void 0 ? note.velocity / MAX_VELOCITY : 1;
161
- const accent = meter === void 0 ? 1 : 1 + _chunkS5BKZZGMcjs.metricWeight.call(void 0, note.startBeat, meter) / ACCENT_DIVISOR;
167
+ const accent = meter === void 0 ? 1 : 1 + _chunkS3QELJUEcjs.metricWeight.call(void 0, note.startBeat, meter) / ACCENT_DIVISOR;
162
168
  return beats * velocityFactor * accent;
163
169
  }
164
170
  function bucketNotesBySlot(notes, grid, slotCount, opts) {
@@ -171,7 +177,7 @@ function bucketNotesBySlot(notes, grid, slotCount, opts) {
171
177
  Math.ceil((note.startBeat + note.durationBeat - grid.origin) / grid.slotBeats)
172
178
  );
173
179
  memberships += Math.max(0, lastExclusive - first);
174
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, memberships, opts.name, opts.budget);
180
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, memberships, opts.name, opts.budget);
175
181
  for (let slot = first; slot < lastExclusive; slot += 1) {
176
182
  _optionalChain([buckets, 'access', _2 => _2[slot], 'optionalAccess', _3 => _3.push, 'call', _4 => _4(note)]);
177
183
  }
@@ -184,14 +190,14 @@ function windowWeights(notes, windowStart, windowEnd, meter) {
184
190
  for (const note of notes) {
185
191
  const noteEnd = note.startBeat + note.durationBeat;
186
192
  const overlap = Math.min(noteEnd, windowEnd) - Math.max(note.startBeat, windowStart);
187
- if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
193
+ if (overlap <= _chunkS3QELJUEcjs.BEAT_EPS) {
188
194
  continue;
189
195
  }
190
- const onsetInWindow = note.startBeat >= windowStart - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < windowEnd - _chunkS5BKZZGMcjs.BEAT_EPS;
191
- const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, note.pitch);
196
+ const onsetInWindow = note.startBeat >= windowStart - _chunkS3QELJUEcjs.BEAT_EPS && note.startBeat < windowEnd - _chunkS3QELJUEcjs.BEAT_EPS;
197
+ const pc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, note.pitch);
192
198
  const weight = noteWeight(note, overlap, onsetInWindow ? meter : void 0);
193
199
  weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + weight;
194
- if (weight > _chunkS5BKZZGMcjs.BEAT_EPS && note.pitch < lowestPitch) {
200
+ if (weight > _chunkS3QELJUEcjs.BEAT_EPS && note.pitch < lowestPitch) {
195
201
  lowestPitch = note.pitch;
196
202
  }
197
203
  }
@@ -204,6 +210,30 @@ function windowWeights(notes, windowStart, windowEnd, meter) {
204
210
  return { weights, totalWeight, maxWeight, lowestPitch };
205
211
  }
206
212
 
213
+ // src/analyze/detect/internal.ts
214
+ var MINOR_VARIANTS = [
215
+ { variant: "natural", mask: _chunkYDGMWH43cjs.NATURAL_MINOR_MASK, scaleName: "naturalMinor" },
216
+ { variant: "harmonic", mask: _chunkYDGMWH43cjs.HARMONIC_MINOR_MASK, scaleName: "harmonicMinor" },
217
+ { variant: "melodic", mask: _chunkYDGMWH43cjs.MELODIC_MINOR_MASK, scaleName: "melodicMinor" }
218
+ ];
219
+ var MAJOR_VARIANTS = [
220
+ { variant: "major", mask: _chunkYDGMWH43cjs.MAJOR_MASK, scaleName: "major" }
221
+ ];
222
+ var SCALE_NAME_ORDER = [
223
+ "major",
224
+ "naturalMinor",
225
+ "harmonicMinor",
226
+ "melodicMinor",
227
+ "dorian",
228
+ "phrygian",
229
+ "lydian",
230
+ "mixolydian",
231
+ "locrian"
232
+ ];
233
+ var EXPLAINED_RIVALS = 3;
234
+ var SCORE_DIGITS = 2;
235
+ var MAX_SCORE_DIGITS = 6;
236
+
207
237
  // src/analyze/detect/profiles.ts
208
238
  var KRUMHANSL_KESSLER_PROFILE = {
209
239
  major: [6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88],
@@ -226,12 +256,12 @@ var NAMED_PROFILES = {
226
256
  };
227
257
  function assertProfileVector(vector, name) {
228
258
  if (vector.length !== PROFILE_LENGTH) {
229
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must have ${PROFILE_LENGTH} entries`);
259
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must have ${PROFILE_LENGTH} entries`);
230
260
  }
231
261
  for (let index = 0; index < vector.length; index += 1) {
232
262
  const value = vector[index];
233
263
  if (typeof value !== "number" || !Number.isFinite(value)) {
234
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}[${index}] must be a finite number`);
264
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}[${index}] must be a finite number`);
235
265
  }
236
266
  }
237
267
  }
@@ -242,18 +272,18 @@ function resolveKeyProfile(profile) {
242
272
  if (typeof profile === "string") {
243
273
  const named = NAMED_PROFILES[profile];
244
274
  if (named === void 0) {
245
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
275
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
246
276
  `unknown key profile '${profile}'; expected one of ${Object.keys(NAMED_PROFILES).join(", ")}`
247
277
  );
248
278
  }
249
279
  return named;
250
280
  }
251
281
  if (typeof profile !== "object" || profile === null) {
252
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("profile must be a profile name or a { major, minor } pair");
282
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)("profile must be a profile name or a { major, minor } pair");
253
283
  }
254
284
  const { major, minor } = profile;
255
285
  if (!Array.isArray(major) || !Array.isArray(minor)) {
256
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("profile must carry a major and a minor vector");
286
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)("profile must carry a major and a minor vector");
257
287
  }
258
288
  assertProfileVector(major, "profile.major");
259
289
  assertProfileVector(minor, "profile.minor");
@@ -302,45 +332,54 @@ function profileScore(dist, vector, tonic) {
302
332
  }
303
333
 
304
334
  // src/analyze/detect/index.ts
335
+ var CHORD_INPUT_KINDS = [
336
+ "auto",
337
+ "midi",
338
+ "pitchClass"
339
+ ];
305
340
  function uniquePitchClasses(pitches) {
306
- return [...new Set(pitches.map(_chunkS5BKZZGMcjs.pitchClassOf))].sort((a, b) => a - b);
341
+ return [...new Set(pitches.map(_chunkS3QELJUEcjs.pitchClassOf))].sort((a, b) => a - b);
307
342
  }
308
343
  function assertPitches(pitches, name, budget = void 0) {
309
- _chunkS5BKZZGMcjs.assertArray.call(void 0, pitches, name);
310
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, pitches.length, name, budget);
344
+ _chunkS3QELJUEcjs.assertArray.call(void 0, pitches, name);
345
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, pitches.length, name, budget);
311
346
  for (let index = 0; index < pitches.length; index += 1) {
312
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, _nullishCoalesce(pitches[index], () => ( Number.NaN)), `pitches[${index}]`);
347
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, _nullishCoalesce(pitches[index], () => ( Number.NaN)), `pitches[${index}]`);
313
348
  }
314
349
  }
315
350
  function detectChord(pitches, opts = {}) {
316
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
351
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
317
352
  assertPitches(pitches, "chord detection pitches");
318
- if (asked.bassPc !== void 0) _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, asked.bassPc, "bassPc");
353
+ const askedKind = _chunkS3QELJUEcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.input, () => ( "auto")), CHORD_INPUT_KINDS, "input");
354
+ if (asked.bassPc !== void 0) _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, asked.bassPc, "bassPc");
319
355
  const input = uniquePitchClasses(pitches);
320
356
  if (input.length === 0) {
321
357
  return [];
322
358
  }
323
- const inputKind = asked.input === void 0 || asked.input === "auto" ? pitches.every((pitch) => Number.isInteger(pitch) && pitch >= 0 && pitch <= 11) ? "pitchClass" : "midi" : asked.input;
324
- const bassPc = asked.bassPc !== void 0 ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, asked.bassPc) : inputKind === "midi" ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
359
+ const inputKind = askedKind === "auto" ? pitches.every((pitch) => Number.isInteger(pitch) && pitch >= 0 && pitch <= 11) ? "pitchClass" : "midi" : askedKind;
360
+ const bassPc = asked.bassPc !== void 0 ? _chunkS3QELJUEcjs.pitchClassOf.call(void 0, asked.bassPc) : inputKind === "midi" ? _chunkS3QELJUEcjs.pitchClassOf.call(void 0, pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
325
361
  const inputSet = new Set(input);
326
362
  const matches = [];
327
- const qualities = _chunk3R3QVKY5cjs.chordQualities.call(void 0, );
363
+ const qualities = _chunkYDGMWH43cjs.chordQualities.call(void 0, );
328
364
  const toneCounts = new Map(
329
- qualities.map((quality) => [quality, _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, 0, quality)).length])
365
+ qualities.map((quality) => [quality, _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, _chunkYDGMWH43cjs.makeChord.call(void 0, 0, quality)).length])
330
366
  );
331
367
  const qualityRank = new Map(qualities.map((quality, index) => [quality, index]));
332
368
  for (const rootPc of input) {
333
369
  for (const quality of qualities) {
334
- const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, quality);
335
- const tones = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord);
370
+ const chord = _chunkYDGMWH43cjs.makeChord.call(void 0, rootPc, quality);
371
+ const tones = _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord);
336
372
  const toneSet = new Set(tones);
337
373
  const missingPcs = tones.filter((pc) => !inputSet.has(pc));
338
- const fifthPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + 7);
374
+ const fifthPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc + 7);
339
375
  if (missingPcs.length > 1 || missingPcs.length === 1 && missingPcs[0] !== fifthPc) {
340
376
  continue;
341
377
  }
378
+ if (tones.length - missingPcs.length < 2) {
379
+ continue;
380
+ }
342
381
  const extraPcs = input.filter((pc) => !toneSet.has(pc));
343
- const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + iv) === bassPc);
382
+ const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc + iv) === bassPc);
344
383
  const inversion = bassPc === void 0 || bassIndex < 0 ? null : bassIndex;
345
384
  const match = {
346
385
  rootPc,
@@ -385,33 +424,11 @@ function detectChordBest(pitches, opts = {}) {
385
424
  if (!best) {
386
425
  return null;
387
426
  }
388
- return _chunk3R3QVKY5cjs.makeChord.call(void 0, best.rootPc, best.quality, best.bassPc);
427
+ return _chunkYDGMWH43cjs.makeChord.call(void 0, best.rootPc, best.quality, best.bassPc);
389
428
  }
390
- var MINOR_VARIANTS = [
391
- { variant: "natural", mask: _chunk3R3QVKY5cjs.NATURAL_MINOR_MASK, scaleName: "naturalMinor" },
392
- { variant: "harmonic", mask: _chunk3R3QVKY5cjs.HARMONIC_MINOR_MASK, scaleName: "harmonicMinor" },
393
- { variant: "melodic", mask: _chunk3R3QVKY5cjs.MELODIC_MINOR_MASK, scaleName: "melodicMinor" }
394
- ];
395
- var MAJOR_VARIANTS = [
396
- { variant: "major", mask: _chunk3R3QVKY5cjs.MAJOR_MASK, scaleName: "major" }
397
- ];
398
- var SCALE_NAME_ORDER = [
399
- "major",
400
- "naturalMinor",
401
- "harmonicMinor",
402
- "melodicMinor",
403
- "dorian",
404
- "phrygian",
405
- "lydian",
406
- "mixolydian",
407
- "locrian"
408
- ];
409
429
  function scaleNameRank(name) {
410
430
  return SCALE_NAME_ORDER.indexOf(name);
411
431
  }
412
- var EXPLAINED_RIVALS = 3;
413
- var SCORE_DIGITS = 2;
414
- var MAX_SCORE_DIGITS = 6;
415
432
  function quoteScores(rival, winner) {
416
433
  let digits = SCORE_DIGITS;
417
434
  while (digits < MAX_SCORE_DIGITS && rival.toFixed(digits) === winner.toFixed(digits)) {
@@ -420,7 +437,7 @@ function quoteScores(rival, winner) {
420
437
  return `${rival.toFixed(digits)} against ${winner.toFixed(digits)}`;
421
438
  }
422
439
  function keyLabel(match) {
423
- const tonic = _chunkS5BKZZGMcjs.formatNote.call(void 0, _chunk3R3QVKY5cjs.spelledKeyOf.call(void 0, match.key).tonic);
440
+ const tonic = _chunkS3QELJUEcjs.formatNote.call(void 0, _chunkYDGMWH43cjs.spelledKeyOf.call(void 0, match.key).tonic);
424
441
  const scale = match.scaleName.replace(/([A-Z])/g, " $1").toLowerCase();
425
442
  return `${tonic} ${scale}`;
426
443
  }
@@ -451,11 +468,11 @@ function explainMatches(results, inputSize) {
451
468
  winner.alternatives = results.slice(1, EXPLAINED_RIVALS + 1).map((rival) => ({ label: keyLabel(rival), reason: keyRivalReason(winner, rival) }));
452
469
  }
453
470
  function detectKey(pitches, opts = {}) {
454
- const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
471
+ const asked = _chunkS3QELJUEcjs.assertRecord.call(void 0, opts, "opts");
455
472
  assertPitches(pitches, "key detection pitches", asked.budget);
456
473
  const weights = asked.weights;
457
474
  if (weights !== void 0 && weights.length !== pitches.length) {
458
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("weights must have one entry per pitch");
475
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)("weights must have one entry per pitch");
459
476
  }
460
477
  const profile = resolveKeyProfile(opts.profile);
461
478
  const modalCandidates = resolveModalCandidates(opts.modes).map((candidate) => ({
@@ -466,11 +483,11 @@ function detectKey(pitches, opts = {}) {
466
483
  let total = 0;
467
484
  for (let index = 0; index < pitches.length; index += 1) {
468
485
  const weight = weights === void 0 ? 1 : _nullishCoalesce(weights[index], () => ( 0));
469
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, weight, `weights[${index}]`);
486
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, weight, `weights[${index}]`);
470
487
  if (weight <= 0) {
471
488
  continue;
472
489
  }
473
- const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(pitches[index], () => ( 0)));
490
+ const pc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(pitches[index], () => ( 0)));
474
491
  counts.set(pc, (_nullishCoalesce(counts.get(pc), () => ( 0))) + weight);
475
492
  total += weight;
476
493
  }
@@ -551,8 +568,8 @@ function detectKey(pitches, opts = {}) {
551
568
  return results;
552
569
  }
553
570
  function detectKeyFromNotes(notes, opts = {}) {
554
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
555
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "key detection notes", {
571
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
572
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "key detection notes", {
556
573
  allowNonPositiveDuration: true,
557
574
  budget: asked.budget
558
575
  });
@@ -574,40 +591,40 @@ function detectKeyBest(pitches, opts = {}) {
574
591
 
575
592
  // src/analyze/functional/internal.ts
576
593
  function isMinorScale(key) {
577
- return _chunk3R3QVKY5cjs.isMinorMask.call(void 0, key.modeMask12);
594
+ return _chunkYDGMWH43cjs.isMinorMask.call(void 0, key.modeMask12);
578
595
  }
579
596
  function parallelScale(key) {
580
597
  return {
581
- rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc),
582
- modeMask12: isMinorScale(key) ? _chunk3R3QVKY5cjs.MAJOR_MASK : _chunk3R3QVKY5cjs.NATURAL_MINOR_MASK
598
+ rootPc: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc),
599
+ modeMask12: isMinorScale(key) ? _chunkYDGMWH43cjs.MAJOR_MASK : _chunkYDGMWH43cjs.NATURAL_MINOR_MASK
583
600
  };
584
601
  }
585
602
  function degreeRootPc(degreeNumber, key) {
586
- const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key);
603
+ const tones = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, key);
587
604
  if (tones.length === 0) {
588
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
605
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc);
589
606
  }
590
607
  const index = ((degreeNumber - 1) % tones.length + tones.length) % tones.length;
591
- return _nullishCoalesce(tones[index], () => ( _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc)));
608
+ return _nullishCoalesce(tones[index], () => ( _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc)));
592
609
  }
593
610
  function loweredDegrees(key) {
594
- const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key);
611
+ const tones = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, key);
595
612
  const out = /* @__PURE__ */ new Set();
596
613
  if (tones.length !== 7) {
597
614
  return out;
598
615
  }
599
- const major = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.MAJOR_MASK });
616
+ const major = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, { rootPc: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkYDGMWH43cjs.MAJOR_MASK });
600
617
  for (let index = 0; index < 7; index += 1) {
601
618
  const tone = tones[index];
602
619
  const majorTone = major[index];
603
- if (tone !== void 0 && majorTone !== void 0 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, majorTone - tone) === 1) {
620
+ if (tone !== void 0 && majorTone !== void 0 && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, majorTone - tone) === 1) {
604
621
  out.add(index + 1);
605
622
  }
606
623
  }
607
624
  return out;
608
625
  }
609
626
  function romanReference(key) {
610
- return _chunk3R3QVKY5cjs.heptatonicFrameOf.call(void 0, key);
627
+ return _chunkYDGMWH43cjs.heptatonicFrameOf.call(void 0, key);
611
628
  }
612
629
 
613
630
  // src/analyze/functional/augmented-sixth.ts
@@ -619,30 +636,31 @@ var TONES_ABOVE_BASS = {
619
636
  german: [0, 4, 7, 10]
620
637
  };
621
638
  var SPELLED_ABOVE_BASS = {
622
- italian: ["P1", "M3", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name)),
623
- french: ["P1", "M3", "A4", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name)),
624
- german: ["P1", "M3", "P5", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name))
639
+ italian: ["P1", "M3", "A6"].map((name) => _chunkS3QELJUEcjs.parseInterval.call(void 0, name)),
640
+ french: ["P1", "M3", "A4", "A6"].map((name) => _chunkS3QELJUEcjs.parseInterval.call(void 0, name)),
641
+ german: ["P1", "M3", "P5", "A6"].map((name) => _chunkS3QELJUEcjs.parseInterval.call(void 0, name))
625
642
  };
626
- var BASS_INTERVAL = _chunkS5BKZZGMcjs.parseInterval.call(void 0, "m6");
643
+ var BASS_INTERVAL = _chunkS3QELJUEcjs.parseInterval.call(void 0, "m6");
627
644
  var CHORD_SHAPE = {
628
645
  italian: { rootAboveBass: 0, quality: "dom7", intervals: [0, 4, 10] },
629
646
  french: { rootAboveBass: 6, quality: "7b5", intervals: [0, 4, 6, 10] },
630
647
  german: { rootAboveBass: 0, quality: "dom7", intervals: [0, 4, 7, 10] }
631
648
  };
632
- var KIND_BY_SYMBOL = /* @__PURE__ */ new Map([
649
+ var KIND_SYMBOLS = [
633
650
  ["It6", "italian"],
634
651
  ["Fr6", "french"],
635
652
  ["Ger6", "german"],
636
653
  // The German sixth's figures over its bass are 6/5; both spellings are in use.
637
654
  ["Ger65", "german"]
638
- ]);
655
+ ];
656
+ var KIND_BY_SYMBOL = new Map(KIND_SYMBOLS);
639
657
  var SYMBOL_BY_KIND = {
640
658
  italian: "It6",
641
659
  french: "Fr6",
642
660
  german: "Ger6"
643
661
  };
644
662
  function bassPcOf(key) {
645
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + BASS_ABOVE_TONIC);
663
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc + BASS_ABOVE_TONIC);
646
664
  }
647
665
  function bareSpelling(note) {
648
666
  return { letter: note.letter, alter: note.alter };
@@ -656,72 +674,74 @@ function augmentedSixthChord(kind, key) {
656
674
  return augmentedSixthOn(kind, key);
657
675
  }
658
676
  function augmentedSixthOn(kind, keyLike) {
659
- const checked = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
677
+ const checked = _chunkS3QELJUEcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
660
678
  const shape = CHORD_SHAPE[checked];
661
- const { scale: key, tonic } = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
679
+ const { scale: key, tonic } = _chunkYDGMWH43cjs.resolveKey.call(void 0, keyLike);
662
680
  const bassPc = bassPcOf(key);
663
- const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bassPc + shape.rootAboveBass), shape.quality, bassPc);
664
- chord.intervals = [...shape.intervals];
665
- const bass = _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
681
+ const bass = _chunkS3QELJUEcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
666
682
  const tones = spelledInChordOrder(checked, tonic);
667
683
  const [root = bass] = tones;
668
- chord.rootSpelling = bareSpelling(root);
669
- chord.bassSpelling = bareSpelling(bass);
670
- chord.toneSpellings = tones.map(bareSpelling);
684
+ const chord = _chunkYDGMWH43cjs.makeSpelledChord.call(void 0, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, bassPc + shape.rootAboveBass), shape.quality, {
685
+ bassPc,
686
+ rootSpelling: bareSpelling(root),
687
+ bassSpelling: bareSpelling(bass),
688
+ toneSpellings: tones.map(bareSpelling)
689
+ });
690
+ chord.intervals = [...shape.intervals];
671
691
  return chord;
672
692
  }
673
693
  function kindOfPitchClasses(pcs, bass) {
674
694
  for (const kind of KINDS) {
675
695
  const tones = TONES_ABOVE_BASS[kind];
676
- if (pcs.size === tones.length && tones.every((tone) => pcs.has(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bass + tone)))) {
696
+ if (pcs.size === tones.length && tones.every((tone) => pcs.has(_chunkS3QELJUEcjs.pitchClassOf.call(void 0, bass + tone)))) {
677
697
  return kind;
678
698
  }
679
699
  }
680
700
  return null;
681
701
  }
682
702
  function spellingId(note) {
683
- return `${_chunkS5BKZZGMcjs.diatonicLetterOf.call(void 0, note.letter)}:${note.alter}`;
703
+ return `${_chunkS3QELJUEcjs.diatonicLetterOf.call(void 0, note.letter)}:${note.alter}`;
684
704
  }
685
705
  function spellsAugmentedSixth(chord, kind, key) {
686
706
  const tonic = key.tonic;
687
- const written = new Set(_chunk3R3QVKY5cjs.spellChord.call(void 0, chord, tonic, key.scale).map(spellingId));
707
+ const written = new Set(_chunkYDGMWH43cjs.spellChord.call(void 0, chord, tonic, key.scale).map(spellingId));
688
708
  const tones = spelledFromTonic(kind, tonic).map(spellingId);
689
709
  return written.size === tones.length && tones.every((tone) => written.has(tone));
690
710
  }
691
711
  function augmentedSixthKind(chord, key) {
692
- return augmentedSixthKindOf(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), key);
712
+ return augmentedSixthKindOf(_chunkYDGMWH43cjs.toChordData.call(void 0, chord), key);
693
713
  }
694
714
  function augmentedSixthKindOf(chord, keyLike) {
695
- const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
715
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, keyLike);
696
716
  const bass = bassPcOf(resolved.scale);
697
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== bass) {
717
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== bass) {
698
718
  return null;
699
719
  }
700
- const kind = kindOfPitchClasses(new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord)), bass);
720
+ const kind = kindOfPitchClasses(new Set(_chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord)), bass);
701
721
  if (kind === null || !spellsAugmentedSixth(chord, kind, resolved)) {
702
722
  return null;
703
723
  }
704
724
  return kind;
705
725
  }
706
726
  function augmentedSixthFromPitchClasses(pcs, bassPc, key) {
707
- return augmentedSixthOverBass(_chunkS5BKZZGMcjs.assertArray.call(void 0, pcs, "pcs"), bassPc, key);
727
+ return augmentedSixthOverBass(_chunkS3QELJUEcjs.assertArray.call(void 0, pcs, "pcs"), bassPc, key);
708
728
  }
709
729
  function augmentedSixthOverBass(pcs, bassPc, keyLike) {
710
- const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
730
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, keyLike);
711
731
  const bass = bassPcOf(resolved.scale);
712
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bassPc) !== bass) {
732
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, bassPc) !== bass) {
713
733
  return null;
714
734
  }
715
- const kind = kindOfPitchClasses(new Set(pcs.map(_chunkS5BKZZGMcjs.pitchClassOf)), bass);
735
+ const kind = kindOfPitchClasses(new Set(pcs.map(_chunkS3QELJUEcjs.pitchClassOf)), bass);
716
736
  return kind === null ? null : augmentedSixthOn(kind, resolved);
717
737
  }
718
738
  function spellAugmentedSixth(kind, tonic) {
719
- return spelledFromTonic(kind, _chunkS5BKZZGMcjs.toNoteData.call(void 0, tonic));
739
+ return spelledFromTonic(kind, _chunkS3QELJUEcjs.toNoteData.call(void 0, tonic));
720
740
  }
721
741
  function spelledFromTonic(kind, tonic) {
722
- const checked = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
723
- const bass = _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
724
- return SPELLED_ABOVE_BASS[checked].map((interval) => _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, bass, interval));
742
+ const checked = _chunkS3QELJUEcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
743
+ const bass = _chunkS3QELJUEcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
744
+ return SPELLED_ABOVE_BASS[checked].map((interval) => _chunkS3QELJUEcjs.transposeByInterval.call(void 0, bass, interval));
725
745
  }
726
746
  function augmentedSixthFromSymbol(symbol, key) {
727
747
  const kind = KIND_BY_SYMBOL.get(symbol);
@@ -737,11 +757,11 @@ var SUBDOMINANT_DEGREE = 4;
737
757
  var DOMINANT_DEGREE = 5;
738
758
  var SUBMEDIANT_DEGREE = 6;
739
759
  function degreeOffset(degreeNumber, key) {
740
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, degreeRootPc(degreeNumber, romanReference(key)) - key.rootPc);
760
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, degreeRootPc(degreeNumber, romanReference(key)) - key.rootPc);
741
761
  }
742
762
  function thirdBelongsToKey(chord, key) {
743
763
  return chord.intervals.some(
744
- (interval) => (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 3 || _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 4) && _chunk3R3QVKY5cjs.isScaleTone.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + interval), key)
764
+ (interval) => (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, interval) === 3 || _chunkS3QELJUEcjs.pitchClassOf.call(void 0, interval) === 4) && _chunkYDGMWH43cjs.isScaleTone.call(void 0, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc + interval), key)
745
765
  );
746
766
  }
747
767
  function deceptiveTargets(key) {
@@ -753,25 +773,25 @@ function deceptiveTargets(key) {
753
773
  return borrowed === submediant ? [submediant] : [submediant, borrowed];
754
774
  }
755
775
  function restsOnDominant(to, key) {
756
- if (_chunk3R3QVKY5cjs.hasMajorThird.call(void 0, to) || _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, to, key)) {
776
+ if (_chunkYDGMWH43cjs.hasMajorThird.call(void 0, to) || _chunkYDGMWH43cjs.isDominantChordOf.call(void 0, to, key)) {
757
777
  return true;
758
778
  }
759
- if (_chunk3R3QVKY5cjs.scaleSystemOf.call(void 0, key) !== "modal") {
779
+ if (_chunkYDGMWH43cjs.scaleSystemOf.call(void 0, key) !== "modal") {
760
780
  return false;
761
781
  }
762
- const dominantPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + degreeOffset(DOMINANT_DEGREE, key));
763
- const leadingToneDominant = _chunk3R3QVKY5cjs.isScaleTone.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, dominantPc + 4), key);
782
+ const dominantPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc + degreeOffset(DOMINANT_DEGREE, key));
783
+ const leadingToneDominant = _chunkYDGMWH43cjs.isScaleTone.call(void 0, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, dominantPc + 4), key);
764
784
  return !leadingToneDominant && thirdBelongsToKey(to, key);
765
785
  }
766
786
  function isTonicSixFour(chord, key) {
767
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) !== 0 || chord.bassPc === void 0) {
787
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) !== 0 || chord.bassPc === void 0) {
768
788
  return false;
769
789
  }
770
- const isTriad = chord.intervals.length === 3 && chord.intervals.some((interval) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 7);
771
- return isTriad && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.bassPc - chord.rootPc) === 7;
790
+ const isTriad = chord.intervals.length === 3 && chord.intervals.some((interval) => _chunkS3QELJUEcjs.pitchClassOf.call(void 0, interval) === 7);
791
+ return isTriad && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.bassPc - chord.rootPc) === 7;
772
792
  }
773
793
  function isCadentialSixFour(chord, next, key) {
774
- return isTonicSixFour(chord, key) && _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, next, key) && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(next.bassPc, () => ( next.rootPc)));
794
+ return isTonicSixFour(chord, key) && _chunkYDGMWH43cjs.isDominantChordOf.call(void 0, next, key) && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(next.bassPc, () => ( next.rootPc)));
775
795
  }
776
796
  function outerPitches(voicing) {
777
797
  let bass;
@@ -790,7 +810,7 @@ function outerPitches(voicing) {
790
810
  return { bass, soprano };
791
811
  }
792
812
  function bassPcOf2(chord, voicedBass) {
793
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(_nullishCoalesce(voicedBass, () => ( chord.bassPc)), () => ( chord.rootPc)));
813
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(_nullishCoalesce(voicedBass, () => ( chord.bassPc)), () => ( chord.rootPc)));
794
814
  }
795
815
  var CADENCE_TYPES = Object.freeze([
796
816
  "authentic",
@@ -801,17 +821,17 @@ var CADENCE_TYPES = Object.freeze([
801
821
  "modal"
802
822
  ]);
803
823
  function cadenceType(from, to, key, fromBassPc, toBassPc) {
804
- const tonic = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
805
- const fromOffset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc - tonic);
806
- const toOffset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - tonic);
807
- const dominant = _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, from, key);
808
- if ((dominant || _chunk3R3QVKY5cjs.isLeadingToneChordOf.call(void 0, from, key)) && toOffset === 0) {
824
+ const tonic = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc);
825
+ const fromOffset = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, from.rootPc - tonic);
826
+ const toOffset = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, to.rootPc - tonic);
827
+ const dominant = _chunkYDGMWH43cjs.isDominantChordOf.call(void 0, from, key);
828
+ if ((dominant || _chunkYDGMWH43cjs.isLeadingToneChordOf.call(void 0, from, key)) && toOffset === 0) {
809
829
  return "authentic";
810
830
  }
811
831
  if (fromOffset === degreeOffset(SUBDOMINANT_DEGREE, key) && toOffset === 0) {
812
832
  return "plagal";
813
833
  }
814
- if (fromOffset === 10 && toOffset === 0 && _chunk3R3QVKY5cjs.hasMajorThird.call(void 0, from)) {
834
+ if (fromOffset === 10 && toOffset === 0 && _chunkYDGMWH43cjs.hasMajorThird.call(void 0, from)) {
815
835
  return "modal";
816
836
  }
817
837
  if (dominant && deceptiveTargets(key).includes(toOffset)) {
@@ -819,7 +839,7 @@ function cadenceType(from, to, key, fromBassPc, toBassPc) {
819
839
  }
820
840
  const dominantOffset = degreeOffset(DOMINANT_DEGREE, key);
821
841
  const half = toOffset === dominantOffset && restsOnDominant(to, key) && fromOffset !== dominantOffset;
822
- if (half && isMinorScale(key) && fromOffset === 5 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, fromBassPc - tonic) === 8 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, toBassPc - tonic) === 7) {
842
+ if (half && isMinorScale(key) && fromOffset === 5 && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, fromBassPc - tonic) === 8 && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, toBassPc - tonic) === 7) {
823
843
  return "phrygian";
824
844
  }
825
845
  return half ? "half" : null;
@@ -921,12 +941,12 @@ function cadenceAlternatives(facts) {
921
941
  return out;
922
942
  }
923
943
  function detectCadence(from, to, key, opts = {}) {
924
- const { approach, ...rest } = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
944
+ const { approach, ...rest } = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
925
945
  return cadenceBetween(
926
- _chunk3R3QVKY5cjs.toChordData.call(void 0, from),
927
- _chunk3R3QVKY5cjs.toChordData.call(void 0, to),
928
- _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key),
929
- approach === void 0 ? rest : { ...rest, approach: _chunk3R3QVKY5cjs.toChordData.call(void 0, approach) }
946
+ _chunkYDGMWH43cjs.toChordData.call(void 0, from),
947
+ _chunkYDGMWH43cjs.toChordData.call(void 0, to),
948
+ _chunkYDGMWH43cjs.toKeyScale.call(void 0, key),
949
+ approach === void 0 ? rest : { ...rest, approach: _chunkYDGMWH43cjs.toChordData.call(void 0, approach) }
930
950
  );
931
951
  }
932
952
  function cadenceBetween(from, to, key, opts = {}) {
@@ -935,11 +955,11 @@ function cadenceBetween(from, to, key, opts = {}) {
935
955
  const toOuter = outerPitches(_nullishCoalesce(toVoicing, () => ( [])));
936
956
  const fromBassPc = bassPcOf2(from, fromOuter.bass);
937
957
  const toBassPc = bassPcOf2(to, toOuter.bass);
938
- const toRootPosition = toBassPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc);
939
- const rootPosition = fromBassPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc) && toRootPosition;
940
- const soprano = toOuter.soprano === void 0 ? null : _chunk3R3QVKY5cjs.chordToneRole.call(void 0, toOuter.soprano, to);
958
+ const toRootPosition = toBassPc === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, to.rootPc);
959
+ const rootPosition = fromBassPc === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, from.rootPc) && toRootPosition;
960
+ const soprano = toOuter.soprano === void 0 ? null : _chunkYDGMWH43cjs.chordToneRole.call(void 0, toOuter.soprano, to);
941
961
  const type = cadenceType(from, to, key, fromBassPc, toBassPc);
942
- const leadingTone = _chunk3R3QVKY5cjs.isLeadingToneChordOf.call(void 0, from, key);
962
+ const leadingTone = _chunkYDGMWH43cjs.isLeadingToneChordOf.call(void 0, from, key);
943
963
  const facts = {
944
964
  type,
945
965
  strength: type === "authentic" ? authenticStrength(leadingTone, rootPosition, toOuter.soprano !== void 0, soprano) : null,
@@ -948,10 +968,10 @@ function cadenceBetween(from, to, key, opts = {}) {
948
968
  // promised is the one it does not get.
949
969
  evaded: type === "authentic" && !toRootPosition,
950
970
  leadingTone,
951
- fromOffset: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc - key.rootPc),
952
- toOffset: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc),
971
+ fromOffset: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, from.rootPc - key.rootPc),
972
+ toOffset: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc),
953
973
  minor: isMinorScale(key),
954
- onFifthDegree: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc) === degreeOffset(DOMINANT_DEGREE, key),
974
+ onFifthDegree: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc) === degreeOffset(DOMINANT_DEGREE, key),
955
975
  sixFour: opts.approach !== void 0 && isCadentialSixFour(opts.approach, from, key),
956
976
  withinSixFour: isCadentialSixFour(from, to, key)
957
977
  };
@@ -972,53 +992,45 @@ function cadenceBetween(from, to, key, opts = {}) {
972
992
  // src/analyze/functional/borrowed.ts
973
993
  function isMinorScaleAlteration(chord, key) {
974
994
  if (!isMinorScale(key)) return false;
975
- const alteredKey = { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.HARMONIC_MINOR_MASK };
976
- if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, alteredKey)) return true;
977
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 11 && _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.MELODIC_MINOR_MASK });
995
+ const alteredKey = { rootPc: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkYDGMWH43cjs.HARMONIC_MINOR_MASK };
996
+ if (_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, alteredKey)) return true;
997
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 11 && _chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, { rootPc: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkYDGMWH43cjs.MELODIC_MINOR_MASK });
978
998
  }
979
999
  function isBorrowed(chord, key) {
980
- if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
1000
+ if (_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, key)) {
981
1001
  return false;
982
1002
  }
983
- if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
1003
+ if (_chunkYDGMWH43cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
984
1004
  return true;
985
1005
  }
986
1006
  if (isMinorScaleAlteration(chord, key)) {
987
1007
  return false;
988
1008
  }
989
- return _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, parallelScale(key));
1009
+ return _chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, parallelScale(key));
990
1010
  }
991
1011
  function borrowedSourceOf(chord, key) {
992
- if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
1012
+ if (_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, key)) {
993
1013
  return null;
994
1014
  }
995
- if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) return "neapolitan";
1015
+ if (_chunkYDGMWH43cjs.isNeapolitanChordOf.call(void 0, chord, key)) return "neapolitan";
996
1016
  if (isBorrowed(chord, key)) {
997
1017
  return isMinorScale(key) ? "parallelMajor" : "parallelMinor";
998
1018
  }
999
1019
  return null;
1000
1020
  }
1001
1021
  function isBorrowedChord(chord, key) {
1002
- return isBorrowed(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
1022
+ return isBorrowed(_chunkYDGMWH43cjs.toChordData.call(void 0, chord), _chunkYDGMWH43cjs.toKeyScale.call(void 0, key));
1003
1023
  }
1004
1024
  function borrowedSource(chord, key) {
1005
- return borrowedSourceOf(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
1025
+ return borrowedSourceOf(_chunkYDGMWH43cjs.toChordData.call(void 0, chord), _chunkYDGMWH43cjs.toKeyScale.call(void 0, key));
1006
1026
  }
1007
1027
 
1008
1028
  // src/analyze/functional/tonicization.ts
1009
1029
  function isTonicizableRoot(rootPc, key) {
1010
- return _chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key).some((degree) => degree.rootPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc));
1011
- }
1012
- function appliedTarget(chord, key) {
1013
- const dominant = _chunk3R3QVKY5cjs.hasDominantSonority.call(void 0, chord);
1014
- const leadingTone = _chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(chord.quality);
1015
- if (!dominant && !leadingTone) {
1016
- return null;
1017
- }
1018
- return _chunk3R3QVKY5cjs.appliedDominantTarget.call(void 0, chord.rootPc + (dominant ? 5 : 1), key);
1030
+ return _chunkYDGMWH43cjs.tonicizableDegrees.call(void 0, key).some((degree) => degree.rootPc === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc));
1019
1031
  }
1020
1032
  function isAppliedDominant(chord, key) {
1021
- if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
1033
+ if (_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, key)) {
1022
1034
  return false;
1023
1035
  }
1024
1036
  const source = borrowedSourceOf(chord, key);
@@ -1028,10 +1040,10 @@ function isAppliedDominant(chord, key) {
1028
1040
  return pointsAtTonicizableDegree(chord, key);
1029
1041
  }
1030
1042
  function pointsAtTonicizableDegree(chord, key) {
1031
- if (appliedTarget(chord, key) !== null) {
1043
+ if (_chunkYDGMWH43cjs.appliedChordTarget.call(void 0, chord, key) !== null) {
1032
1044
  return true;
1033
1045
  }
1034
- return _chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, chord) && isTonicizableRoot(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - 1), key);
1046
+ return _chunkYDGMWH43cjs.soundsDominantSeventh.call(void 0, chord) && isTonicizableRoot(_chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - 1), key);
1035
1047
  }
1036
1048
 
1037
1049
  // src/analyze/functional/roman.ts
@@ -1071,7 +1083,8 @@ function seventhQuality(base, halfDim) {
1071
1083
  }
1072
1084
  return base === "maj" ? "dom7" : "min7";
1073
1085
  }
1074
- var INVERSION_FIGURES = /* @__PURE__ */ new Set(["6", "64", "65", "43", "42", "2", "7"]);
1086
+ var INVERSION_FIGURE_LIST = ["6", "64", "65", "43", "42", "2", "7"];
1087
+ var INVERSION_FIGURES = new Set(INVERSION_FIGURE_LIST);
1075
1088
  var ROMAN_STYLE = {
1076
1089
  maj: { lower: false, suffix: "" },
1077
1090
  min: { lower: true, suffix: "" },
@@ -1143,36 +1156,50 @@ function parseInversion(figures) {
1143
1156
  }
1144
1157
  return { inversion: 0, seventh: false };
1145
1158
  }
1146
- function parseSimpleRoman(text, key) {
1159
+ function numeralRootSpelling(tonic, degreeNumber, rootPc) {
1160
+ const spelling = _chunkYDGMWH43cjs.spellingAbove.call(void 0, tonic, degreeNumber - 1, rootPc);
1161
+ return Math.abs(spelling.alter) <= 2 ? spelling : void 0;
1162
+ }
1163
+ function parseSimpleRoman(text, resolved) {
1164
+ const key = resolved.scale;
1147
1165
  const match = /^([b#]?)([iIvV]+)(.*)$/.exec(text.trim());
1148
1166
  if (!match) {
1149
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
1167
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
1150
1168
  }
1151
1169
  const glyphText = _nullishCoalesce(match[2], () => ( ""));
1152
1170
  if (glyphText !== glyphText.toUpperCase() && glyphText !== glyphText.toLowerCase()) {
1153
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Roman numeral must use one case: ${text}`);
1171
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`Roman numeral must use one case: ${text}`);
1154
1172
  }
1155
1173
  const accidental = match[1] === "b" ? -1 : match[1] === "#" ? 1 : 0;
1156
1174
  const glyph = glyphText.toUpperCase();
1157
1175
  const degreeNumber = ROMAN_TO_DEGREE[glyph];
1158
1176
  if (degreeNumber === void 0) {
1159
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
1177
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
1160
1178
  }
1161
1179
  const isUpper = glyphText[0] === _optionalChain([glyphText, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.toUpperCase, 'call', _7 => _7()]);
1162
1180
  const suffix = _nullishCoalesce(match[3], () => ( ""));
1163
1181
  const frame = romanReference(key);
1164
1182
  const redundantFlat = accidental === -1 && loweredDegrees(frame).has(degreeNumber);
1165
- const diatonicRootPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0,
1183
+ const diatonicRootPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0,
1166
1184
  degreeRootPc(degreeNumber, frame) + (redundantFlat ? 0 : accidental)
1167
1185
  );
1168
- const rootForQuality = (quality2) => accidental === 0 && degreeNumber === 7 && loweredDegrees(frame).has(7) && _chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(quality2) ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, diatonicRootPc + 1) : diatonicRootPc;
1186
+ const rootForQuality = (quality2) => accidental === 0 && degreeNumber === 7 && loweredDegrees(frame).has(7) && _chunkYDGMWH43cjs.LEADING_TONE_QUALITIES.has(quality2) ? _chunkS3QELJUEcjs.pitchClassOf.call(void 0, diatonicRootPc + 1) : diatonicRootPc;
1169
1187
  const canonical = SUFFIX_QUALITY.get(`${isUpper ? "u" : "l"}:${suffix}`);
1188
+ const parsed = (quality2, inversion2) => {
1189
+ const rootPc = rootForQuality(quality2);
1190
+ return {
1191
+ rootPc,
1192
+ rootSpelling: numeralRootSpelling(resolved.tonic, degreeNumber, rootPc),
1193
+ quality: quality2,
1194
+ inversion: inversion2
1195
+ };
1196
+ };
1170
1197
  if (canonical !== void 0) {
1171
- return { rootPc: rootForQuality(canonical), quality: canonical, inversion: 0 };
1198
+ return parsed(canonical, 0);
1172
1199
  }
1173
1200
  const parsedSuffix = /^(o|°|dim|ø|\+|aug)?(maj7|M7)?(65|64|43|42|7|6|2)?$/.exec(suffix);
1174
1201
  if (!parsedSuffix) {
1175
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
1202
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
1176
1203
  }
1177
1204
  const qualityMarker = _nullishCoalesce(parsedSuffix[1], () => ( ""));
1178
1205
  const isDim = qualityMarker === "o" || qualityMarker === "\xB0" || qualityMarker === "dim";
@@ -1181,7 +1208,7 @@ function parseSimpleRoman(text, key) {
1181
1208
  const explicitMaj7 = parsedSuffix[2] !== void 0;
1182
1209
  const figures = _nullishCoalesce(parsedSuffix[3], () => ( ""));
1183
1210
  if (figures !== "" && !INVERSION_FIGURES.has(figures)) {
1184
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
1211
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
1185
1212
  `Unsupported figured-bass or extension "${figures}" in Roman numeral: ${text}`
1186
1213
  );
1187
1214
  }
@@ -1207,27 +1234,30 @@ function parseSimpleRoman(text, key) {
1207
1234
  } else {
1208
1235
  quality = base;
1209
1236
  }
1210
- return { rootPc: rootForQuality(quality), quality, inversion };
1237
+ return parsed(quality, inversion);
1211
1238
  }
1212
1239
  var NEAPOLITAN_SIXTH = "N6";
1213
1240
  function chromaticSymbolChord(text, key) {
1214
1241
  if (text === NEAPOLITAN_SIXTH) {
1215
- const rootPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.scale.rootPc + 1);
1216
- return _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, "maj", _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + 4));
1242
+ const rootPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.scale.rootPc + 1);
1243
+ return _chunkYDGMWH43cjs.makeSpelledChord.call(void 0, rootPc, "maj", {
1244
+ bassPc: _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc + 4),
1245
+ rootSpelling: numeralRootSpelling(key.tonic, 2, rootPc)
1246
+ });
1217
1247
  }
1218
1248
  return augmentedSixthFromSymbol(text, key);
1219
1249
  }
1220
- function tonicizedKey(targetRoot, key) {
1221
- return _chunk3R3QVKY5cjs.resolveKey.call(void 0, {
1222
- scale: _chunk3R3QVKY5cjs.majorKey.call(void 0, targetRoot),
1223
- tonic: _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, targetRoot, key.tonic, key.scale)
1250
+ function tonicizedKey(targetRoot, key, written) {
1251
+ return _chunkYDGMWH43cjs.resolveKey.call(void 0, {
1252
+ scale: _chunkYDGMWH43cjs.majorKey.call(void 0, targetRoot),
1253
+ tonic: _nullishCoalesce(written, () => ( _chunkYDGMWH43cjs.spellPitchClass.call(void 0, targetRoot, key.tonic, key.scale)))
1224
1254
  });
1225
1255
  }
1226
1256
  function augmentedSixthTarget(chord, key) {
1227
- if (_chunk3R3QVKY5cjs.chordToneSpellings.call(void 0, chord) === void 0) {
1257
+ if (_chunkYDGMWH43cjs.chordToneSpellings.call(void 0, chord) === void 0) {
1228
1258
  return null;
1229
1259
  }
1230
- for (const degree of _chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key.scale)) {
1260
+ for (const degree of _chunkYDGMWH43cjs.tonicizableDegrees.call(void 0, key.scale)) {
1231
1261
  if (degree.degreeNumber === 1) {
1232
1262
  continue;
1233
1263
  }
@@ -1238,60 +1268,64 @@ function augmentedSixthTarget(chord, key) {
1238
1268
  return null;
1239
1269
  }
1240
1270
  function chordFromParsed(parsed) {
1241
- const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, parsed.rootPc, parsed.quality);
1242
- if (parsed.inversion > 0 && parsed.inversion < chord.intervals.length) {
1243
- chord.bassPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, parsed.rootPc + (_nullishCoalesce(chord.intervals[parsed.inversion], () => ( 0))));
1244
- }
1245
- return chord;
1271
+ const bassInterval = parsed.inversion > 0 ? _chunkYDGMWH43cjs.QUALITY_INTERVALS[parsed.quality][parsed.inversion] : void 0;
1272
+ return _chunkYDGMWH43cjs.makeSpelledChord.call(void 0, parsed.rootPc, parsed.quality, {
1273
+ bassPc: bassInterval === void 0 ? void 0 : _chunkS3QELJUEcjs.pitchClassOf.call(void 0, parsed.rootPc + bassInterval),
1274
+ rootSpelling: parsed.rootSpelling
1275
+ });
1246
1276
  }
1247
1277
  function romanToChord(text, key) {
1248
- const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, key);
1249
- const scale = resolved.scale;
1278
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, key);
1279
+ if (typeof text !== "string") {
1280
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
1281
+ `Roman numeral must be a string; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, text)}`
1282
+ );
1283
+ }
1250
1284
  const trimmed = text.trim().replace(/(\d)\/(?=\d)/g, "$1");
1251
1285
  const slash = trimmed.indexOf("/");
1252
1286
  if (slash >= 0) {
1253
1287
  const applied = trimmed.slice(0, slash);
1254
1288
  const target = trimmed.slice(slash + 1);
1255
- const targetRoot = parseSimpleRoman(target, scale).rootPc;
1256
- const localKey = tonicizedKey(targetRoot, resolved);
1257
- return _nullishCoalesce(chromaticSymbolChord(applied, localKey), () => ( chordFromParsed(parseSimpleRoman(applied, localKey.scale))));
1289
+ const targetNumeral = parseSimpleRoman(target, resolved);
1290
+ const localKey = tonicizedKey(targetNumeral.rootPc, resolved, targetNumeral.rootSpelling);
1291
+ return _nullishCoalesce(chromaticSymbolChord(applied, localKey), () => ( chordFromParsed(parseSimpleRoman(applied, localKey))));
1258
1292
  }
1259
- return _nullishCoalesce(chromaticSymbolChord(trimmed, resolved), () => ( chordFromParsed(parseSimpleRoman(trimmed, scale))));
1293
+ return _nullishCoalesce(chromaticSymbolChord(trimmed, resolved), () => ( chordFromParsed(parseSimpleRoman(trimmed, resolved))));
1260
1294
  }
1261
1295
  function romanStyle(quality) {
1262
1296
  return ROMAN_STYLE[quality];
1263
1297
  }
1264
- function romanSpelling(rootPc, key) {
1265
- const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
1266
- const diatonic = tones.indexOf(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc));
1298
+ function romanSpelling(rootPc, key, quality) {
1299
+ const tones = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
1300
+ const diatonic = tones.indexOf(_chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc));
1267
1301
  if (diatonic >= 0) {
1268
1302
  return { degreeNumber: diatonic + 1, accidental: "" };
1269
1303
  }
1270
1304
  const lowered = loweredDegrees(romanReference(key));
1271
1305
  for (let i = 0; i < tones.length; i += 1) {
1272
- if (lowered.has(i + 1) && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
1306
+ if (lowered.has(i + 1) && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc)) {
1273
1307
  return { degreeNumber: i + 1, accidental: "#" };
1274
1308
  }
1275
1309
  }
1276
- for (let i = 0; i < tones.length; i += 1) {
1310
+ for (let i = 0; i < tones.length && !_chunkYDGMWH43cjs.LEADING_TONE_QUALITIES.has(quality); i += 1) {
1277
1311
  const degreeNumber2 = i + 1;
1278
1312
  if (degreeNumber2 === 1 || degreeNumber2 === 5) {
1279
1313
  continue;
1280
1314
  }
1281
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) - 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
1315
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) - 1) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc)) {
1282
1316
  return { degreeNumber: degreeNumber2, accidental: "b" };
1283
1317
  }
1284
1318
  }
1285
1319
  for (let i = 0; i < tones.length; i += 1) {
1286
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
1320
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc)) {
1287
1321
  return { degreeNumber: i + 1, accidental: "#" };
1288
1322
  }
1289
1323
  }
1290
- const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc - key.rootPc)], () => ( [1, ""]));
1324
+ const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[_chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc - key.rootPc)], () => ( [1, ""]));
1291
1325
  return { degreeNumber, accidental };
1292
1326
  }
1293
1327
  function seventhDegreeAccidental(accidental, degreeNumber, quality, key) {
1294
- if (degreeNumber !== 7 || !_chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(quality) || !loweredDegrees(romanReference(key)).has(7)) {
1328
+ if (degreeNumber !== 7 || !_chunkYDGMWH43cjs.LEADING_TONE_QUALITIES.has(quality) || !loweredDegrees(romanReference(key)).has(7)) {
1295
1329
  return accidental;
1296
1330
  }
1297
1331
  if (accidental === "#") {
@@ -1319,8 +1353,9 @@ function baseMarker(quality) {
1319
1353
  }
1320
1354
  var TRIAD_FIGURES = { 1: "6", 2: "64" };
1321
1355
  var SEVENTH_FIGURES = { 1: "65", 2: "43", 3: "42" };
1322
- var TRIAD_FIGURE_QUALITIES = /* @__PURE__ */ new Set(["maj", "min", "dim", "aug"]);
1323
- var SEVENTH_FIGURE_QUALITIES = /* @__PURE__ */ new Set([
1356
+ var TRIAD_FIGURE_QUALITY_NAMES = ["maj", "min", "dim", "aug"];
1357
+ var TRIAD_FIGURE_QUALITIES = new Set(TRIAD_FIGURE_QUALITY_NAMES);
1358
+ var SEVENTH_FIGURE_QUALITY_NAMES = [
1324
1359
  "maj7",
1325
1360
  "min7",
1326
1361
  "dom7",
@@ -1329,50 +1364,90 @@ var SEVENTH_FIGURE_QUALITIES = /* @__PURE__ */ new Set([
1329
1364
  "minMaj7",
1330
1365
  "aug7",
1331
1366
  "augMaj7"
1332
- ]);
1367
+ ];
1368
+ var SEVENTH_FIGURE_QUALITIES = new Set(SEVENTH_FIGURE_QUALITY_NAMES);
1333
1369
  function numeralFor(degreeNumber, lower) {
1334
1370
  const glyph = ROMAN[degreeNumber - 1];
1335
1371
  if (glyph === void 0) {
1336
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`no Roman numeral for scale degree ${degreeNumber}`);
1372
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`no Roman numeral for scale degree ${degreeNumber}`);
1337
1373
  }
1338
1374
  return lower ? glyph.toLowerCase() : glyph;
1339
1375
  }
1340
1376
  function chordToRoman(chord, key, opts = {}) {
1341
- return renderRoman(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), key, _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts")).roman;
1377
+ return renderRoman(_chunkYDGMWH43cjs.toChordData.call(void 0, chord), key, _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts")).roman;
1378
+ }
1379
+ function readNumeral(chord, resolved, applied) {
1380
+ const key = resolved.scale;
1381
+ const written = writtenRoot(chord);
1382
+ if (applied) {
1383
+ const target = _nullishCoalesce(augmentedSixthTarget(chord, resolved), () => ( (isAppliedDominant(chord, key) ? _chunkYDGMWH43cjs.appliedChordTarget.call(void 0, chord, key) : null)));
1384
+ if (target !== null && leadsByLetter(chord, written, target, resolved)) {
1385
+ return { kind: "applied", target };
1386
+ }
1387
+ }
1388
+ return {
1389
+ kind: "root",
1390
+ ..._nullishCoalesce(writtenSpelling(chord.rootPc, written, resolved), () => ( romanSpelling(chord.rootPc, key, chord.quality)))
1391
+ };
1392
+ }
1393
+ function writtenRoot(chord) {
1394
+ const hint = chord.rootSpelling;
1395
+ if (typeof hint !== "object" || hint === null || !Number.isInteger(hint.letter) || hint.letter < 0 || hint.letter > 6 || !Number.isInteger(hint.alter) || _chunkS3QELJUEcjs.pitchClassOf.call(void 0, _chunkS3QELJUEcjs.naturalPitchClassOf.call(void 0, hint.letter) + hint.alter) !== _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc)) {
1396
+ return void 0;
1397
+ }
1398
+ return hint;
1399
+ }
1400
+ function writtenSpelling(rootPc, written, resolved) {
1401
+ const tones = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(resolved.scale));
1402
+ if (written === void 0 || tones.includes(_chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc))) {
1403
+ return void 0;
1404
+ }
1405
+ const degreeNumber = _chunkS3QELJUEcjs.diatonicLetterOf.call(void 0, written.letter - resolved.tonic.letter) + 1;
1406
+ const degreePc = tones[degreeNumber - 1];
1407
+ if (degreePc === void 0) {
1408
+ return void 0;
1409
+ }
1410
+ const offset = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, rootPc - degreePc + 6) - 6;
1411
+ return Math.abs(offset) > 1 ? void 0 : { degreeNumber, accidental: offset < 0 ? "b" : offset > 0 ? "#" : "" };
1412
+ }
1413
+ function leadsByLetter(chord, written, target, resolved) {
1414
+ if (written === void 0 || !_chunkYDGMWH43cjs.LEADING_TONE_QUALITIES.has(chord.quality)) {
1415
+ return true;
1416
+ }
1417
+ const targetLetter = _chunkYDGMWH43cjs.spellPitchClass.call(void 0, target.rootPc, resolved.tonic, resolved.scale).letter;
1418
+ return _chunkS3QELJUEcjs.diatonicLetterOf.call(void 0, targetLetter - written.letter) === 1;
1342
1419
  }
1343
1420
  function renderRoman(chord, keyLike, opts) {
1344
- const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
1421
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, keyLike);
1345
1422
  const key = resolved.scale;
1346
1423
  const augmented = augmentedSixthSymbol(chord, resolved);
1347
1424
  if (augmented !== null) {
1348
1425
  return { roman: augmented, derivation: { kind: "augmentedSixth" } };
1349
1426
  }
1350
- if (opts.applied === true) {
1351
- const target = _nullishCoalesce(augmentedSixthTarget(chord, resolved), () => ( (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key) ? null : appliedTarget(chord, key))));
1352
- if (target !== null) {
1353
- const local = renderRoman(chord, tonicizedKey(target.rootPc, resolved), {}).roman;
1354
- const numeral = numeralFor(target.degreeNumber, target.lower);
1355
- return {
1356
- roman: `${local}/${numeral}`,
1357
- derivation: { kind: "applied", target: numeral }
1358
- };
1359
- }
1427
+ const reading = readNumeral(chord, resolved, opts.applied === true);
1428
+ if (reading.kind === "applied") {
1429
+ const local = renderRoman(chord, tonicizedKey(reading.target.rootPc, resolved), {}).roman;
1430
+ const numeral = numeralFor(reading.target.degreeNumber, reading.target.lower);
1431
+ return {
1432
+ roman: `${local}/${numeral}`,
1433
+ derivation: { kind: "applied", target: numeral }
1434
+ };
1360
1435
  }
1361
- const { degreeNumber, accidental: spelledAccidental } = romanSpelling(chord.rootPc, key);
1436
+ const { degreeNumber, accidental: spelledAccidental } = reading;
1362
1437
  const { lower, suffix } = romanStyle(chord.quality);
1363
1438
  const cased = numeralFor(degreeNumber, lower);
1364
1439
  const accidental = seventhDegreeAccidental(spelledAccidental, degreeNumber, chord.quality, key);
1365
1440
  let inversion = 0;
1366
1441
  let bass = "root";
1367
1442
  if (chord.bassPc !== void 0) {
1368
- const idx = chord.intervals.findIndex((iv) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === chord.bassPc);
1443
+ const idx = chord.intervals.findIndex((iv) => _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === chord.bassPc);
1369
1444
  if (idx > 0) {
1370
1445
  inversion = idx;
1371
1446
  } else if (idx < 0) {
1372
1447
  bass = "foreign";
1373
1448
  }
1374
1449
  }
1375
- if (opts.neapolitan === true && inversion === 1 && _chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
1450
+ if (opts.neapolitan === true && inversion === 1 && _chunkYDGMWH43cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
1376
1451
  return { roman: NEAPOLITAN_SIXTH, derivation: { kind: "neapolitan" } };
1377
1452
  }
1378
1453
  if (inversion > 0) {
@@ -1405,7 +1480,15 @@ function renderRoman(chord, keyLike, opts) {
1405
1480
  }
1406
1481
  };
1407
1482
  }
1408
- var DEGREE_NAMES = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh"];
1483
+ var DEGREE_NAMES = [
1484
+ "first",
1485
+ "second",
1486
+ "third",
1487
+ "fourth",
1488
+ "fifth",
1489
+ "sixth",
1490
+ "seventh"
1491
+ ];
1409
1492
  function describeDegree(degreeNumber, accidental, literal) {
1410
1493
  const name = _nullishCoalesce(DEGREE_NAMES[degreeNumber - 1], () => ( `${degreeNumber}th`));
1411
1494
  if (accidental !== literal) {
@@ -1471,8 +1554,8 @@ function romanAlternatives(chord, key, opts) {
1471
1554
  return out;
1472
1555
  }
1473
1556
  function explainRoman(chord, key, opts = {}) {
1474
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
1475
- const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
1557
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
1558
+ const data = _chunkYDGMWH43cjs.toChordData.call(void 0, chord);
1476
1559
  const { roman, derivation } = renderRoman(data, key, asked);
1477
1560
  return {
1478
1561
  roman,
@@ -1541,27 +1624,28 @@ var MINOR_FUNCTION_BY_OFFSET = [
1541
1624
  // 11 viio
1542
1625
  ];
1543
1626
  function isMinorKey(key) {
1544
- return isMinorScale(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
1627
+ return isMinorScale(_chunkYDGMWH43cjs.toKeyScale.call(void 0, key));
1545
1628
  }
1546
1629
  function functionOf(chord, key) {
1547
- return functionWithReason(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key)).function;
1630
+ return functionWithReason(_chunkYDGMWH43cjs.toChordData.call(void 0, chord), _chunkYDGMWH43cjs.resolveKey.call(void 0, key)).function;
1548
1631
  }
1549
1632
  function degreeFunction(chord, key) {
1550
1633
  const functions = isMinorScale(key) ? MINOR_FUNCTION_BY_OFFSET : FUNCTION_BY_OFFSET;
1551
- return _nullishCoalesce(functions[_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc)], () => ( "tonic"));
1634
+ return _nullishCoalesce(functions[_chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc)], () => ( "tonic"));
1552
1635
  }
1553
- function functionWithReason(chord, key) {
1554
- const offset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc);
1555
- if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
1636
+ function functionWithReason(chord, resolved) {
1637
+ const key = resolved.scale;
1638
+ const offset = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc);
1639
+ if (_chunkYDGMWH43cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
1556
1640
  return { function: "subdominant", reason: "neapolitan" };
1557
1641
  }
1558
- if (augmentedSixthKindOf(chord, key) !== null) {
1642
+ if (augmentedSixthKindOf(chord, resolved) !== null) {
1559
1643
  return { function: "subdominant", reason: "augmentedSixth" };
1560
1644
  }
1561
1645
  if (isAppliedDominant(chord, key)) {
1562
1646
  return { function: "dominant", reason: "applied" };
1563
1647
  }
1564
- if (!isMinorScale(key) && !_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key) && _chunk3R3QVKY5cjs.hasMajorThird.call(void 0, chord) && (offset === 8 || offset === 10)) {
1648
+ if (!isMinorScale(key) && !_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, key) && _chunkYDGMWH43cjs.hasMajorThird.call(void 0, chord) && (offset === 8 || offset === 10)) {
1565
1649
  return { function: "subdominant", reason: "flatSideMajor" };
1566
1650
  }
1567
1651
  return { function: degreeFunction(chord, key), reason: "degree" };
@@ -1581,10 +1665,11 @@ function describeChord(fn, reason, roman, source) {
1581
1665
  const head = reason === "degree" ? `${capitalize(fn)}: ${roman} takes the ${fn} function of its degree in the key` : `${capitalize(fn)}: ${roman} is ${FUNCTION_REASON_PHRASE[reason]}`;
1582
1666
  return source === null || reason === "neapolitan" ? head : `${head}, ${BORROWED_SOURCE_PHRASE[source]}`;
1583
1667
  }
1584
- function functionAlternatives(chord, key, reason, accepted) {
1668
+ function functionAlternatives(chord, resolved, reason, accepted) {
1669
+ const key = resolved.scale;
1585
1670
  const out = [];
1586
1671
  if (reason === "degree") {
1587
- if (pointsAtTonicizableDegree(chord, key) && _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
1672
+ if (pointsAtTonicizableDegree(chord, key) && _chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, key)) {
1588
1673
  out.push({
1589
1674
  label: "applied dominant",
1590
1675
  reason: "The chord is diatonic to the key, so it keeps the function of its degree rather than tonicizing another"
@@ -1599,7 +1684,7 @@ function functionAlternatives(chord, key, reason, accepted) {
1599
1684
  });
1600
1685
  }
1601
1686
  }
1602
- if (augmentedSixthKindOf(chord, key) === "german") {
1687
+ if (augmentedSixthKindOf(chord, resolved) === "german") {
1603
1688
  out.push({
1604
1689
  label: "tritone substitute",
1605
1690
  reason: "The German sixth sounds a dominant seventh, but the lowered submediant under it fixes the reading as an augmented sixth"
@@ -1608,18 +1693,18 @@ function functionAlternatives(chord, key, reason, accepted) {
1608
1693
  return out;
1609
1694
  }
1610
1695
  function isDiatonic(chord, key) {
1611
- return _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, _chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
1696
+ return _chunkYDGMWH43cjs.isDiatonicChord.call(void 0, _chunkYDGMWH43cjs.toChordData.call(void 0, chord), _chunkYDGMWH43cjs.toKeyScale.call(void 0, key));
1612
1697
  }
1613
1698
  function parallelKey(key) {
1614
- return parallelScale(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
1699
+ return parallelScale(_chunkYDGMWH43cjs.toKeyScale.call(void 0, key));
1615
1700
  }
1616
1701
  function analyzeChord(chord, key, opts = {}) {
1617
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
1618
- const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
1619
- const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
1620
- const source = borrowedSourceOf(data, scale);
1621
- const { function: fn, reason } = functionWithReason(data, scale);
1622
- const roman = renderRoman(data, scale, asked).roman;
1702
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
1703
+ const data = _chunkYDGMWH43cjs.toChordData.call(void 0, chord);
1704
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, key);
1705
+ const source = borrowedSourceOf(data, resolved.scale);
1706
+ const { function: fn, reason } = functionWithReason(data, resolved);
1707
+ const roman = renderRoman(data, resolved, asked).roman;
1623
1708
  return {
1624
1709
  function: fn,
1625
1710
  borrowed: source !== null,
@@ -1627,43 +1712,41 @@ function analyzeChord(chord, key, opts = {}) {
1627
1712
  roman,
1628
1713
  rationale: describeChord(fn, reason, roman, source),
1629
1714
  alternatives: asked.alternatives === true ? [
1630
- ...functionAlternatives(data, scale, reason, fn),
1631
- ...romanAlternatives(data, scale, asked)
1715
+ ...functionAlternatives(data, resolved, reason, fn),
1716
+ ...romanAlternatives(data, resolved, asked)
1632
1717
  ] : []
1633
1718
  };
1634
1719
  }
1635
1720
  function secondaryDominant(targetDegree, key) {
1636
- const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
1637
- const degreeCount = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, scale).length;
1638
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, targetDegree, "targetDegree", 1, degreeCount);
1721
+ const resolved = _chunkYDGMWH43cjs.resolveKey.call(void 0, key);
1722
+ const scale = resolved.scale;
1723
+ const degreeCount = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, scale).length;
1724
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, targetDegree, "targetDegree", 1, degreeCount);
1639
1725
  const targetRoot = degreeRootPc(targetDegree, scale);
1640
- return _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, targetRoot + 7), "dom7");
1726
+ const rootSpelling = _chunkS3QELJUEcjs.transposeByInterval.call(void 0, _chunkYDGMWH43cjs.keySpelling.call(void 0, targetRoot, resolved), "P5");
1727
+ return _chunkYDGMWH43cjs.makeSpelledChord.call(void 0, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, targetRoot + 7), "dom7", { rootSpelling });
1641
1728
  }
1642
1729
  function secondaryDominantOf(target) {
1643
- const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, target);
1644
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, data.rootPc, "target chord rootPc");
1645
- const dominant = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, data.rootPc + 7), "dom7");
1646
- if (data.rootSpelling !== void 0) {
1647
- const moved = _chunkS5BKZZGMcjs.transposeNote.call(void 0, data.rootSpelling, 7);
1648
- dominant.rootSpelling = { letter: moved.letter, alter: moved.alter };
1649
- }
1650
- return dominant;
1730
+ const data = _chunkYDGMWH43cjs.toChordData.call(void 0, target);
1731
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, data.rootPc, "target chord rootPc");
1732
+ const rootSpelling = data.rootSpelling === void 0 ? void 0 : _chunkS3QELJUEcjs.transposeNote.call(void 0, data.rootSpelling, 7);
1733
+ return _chunkYDGMWH43cjs.makeSpelledChord.call(void 0, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, data.rootPc + 7), "dom7", { rootSpelling });
1651
1734
  }
1652
1735
 
1653
1736
  // src/analyze/functional/pivot.ts
1654
1737
  var HEPTATONIC_DEGREES = 7;
1655
1738
  function pivotChords(from, to) {
1656
- return pivotsBetween(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, from), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, to));
1739
+ return pivotsBetween(_chunkYDGMWH43cjs.resolveKey.call(void 0, from), _chunkYDGMWH43cjs.resolveKey.call(void 0, to));
1657
1740
  }
1658
1741
  function pivotsBetween(from, to) {
1659
- if (_chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, from).length !== HEPTATONIC_DEGREES) {
1742
+ if (_chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, from.scale).length !== HEPTATONIC_DEGREES) {
1660
1743
  return [];
1661
1744
  }
1662
1745
  const pivots = [];
1663
1746
  const seen = /* @__PURE__ */ new Set();
1664
1747
  for (let degree = 1; degree <= HEPTATONIC_DEGREES; degree += 1) {
1665
1748
  const chord = stackedTriad(degree, from);
1666
- if (chord === null || !_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, to)) {
1749
+ if (chord === null || !_chunkYDGMWH43cjs.isDiatonicChord.call(void 0, chord, to.scale)) {
1667
1750
  continue;
1668
1751
  }
1669
1752
  const identity = `${chord.rootPc}:${chord.quality}`;
@@ -1681,9 +1764,9 @@ function pivotsBetween(from, to) {
1681
1764
  }
1682
1765
  function stackedTriad(degree, key) {
1683
1766
  try {
1684
- return _chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, key);
1767
+ return _chunkYDGMWH43cjs.diatonicTriad.call(void 0, degree, key);
1685
1768
  } catch (error) {
1686
- if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
1769
+ if (error instanceof _chunkS3QELJUEcjs.InvalidInputError) {
1687
1770
  return null;
1688
1771
  }
1689
1772
  throw error;
@@ -1692,11 +1775,11 @@ function stackedTriad(degree, key) {
1692
1775
 
1693
1776
  // src/analyze/form/internal.ts
1694
1777
  function barSpanOf(startBeat, endBeat, meter) {
1695
- const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meter);
1696
- const lastBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, endBeat, meter);
1778
+ const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, startBeat, meter);
1779
+ const lastBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, endBeat, meter);
1697
1780
  const partOf = (beat, bar) => {
1698
- const barStart = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
1699
- const barLength = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, barStart, meter);
1781
+ const barStart = _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
1782
+ const barLength = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, barStart, meter);
1700
1783
  return barLength > 0 ? (beat - barStart) / barLength : 0;
1701
1784
  };
1702
1785
  return lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);
@@ -1714,35 +1797,43 @@ function firstSoundingBeat(notes, fallback = 0) {
1714
1797
  }
1715
1798
  return Number.isFinite(first) ? first : fallback;
1716
1799
  }
1800
+ function resolveSpanEnd(totalBeats, startBeat, soundingEnd, name) {
1801
+ const given = _nullishCoalesce(totalBeats, () => ( null));
1802
+ if (given === null) {
1803
+ return Math.max(startBeat, soundingEnd);
1804
+ }
1805
+ _chunkS3QELJUEcjs.assertRange.call(void 0, given, Math.min(0, startBeat), Number.MAX_SAFE_INTEGER, name);
1806
+ return Math.max(startBeat, given);
1807
+ }
1717
1808
  function lastBarOf(meter, spanStart, spanEnd) {
1718
- const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, spanStart, meter);
1719
- const bar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(spanStart, spanEnd), meter);
1720
- if (bar > firstBar && _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter) >= spanEnd - _chunkS5BKZZGMcjs.BEAT_EPS) {
1809
+ const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, spanStart, meter);
1810
+ const bar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, Math.max(spanStart, spanEnd), meter);
1811
+ if (bar > firstBar && _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter) >= spanEnd - _chunkS3QELJUEcjs.BEAT_EPS) {
1721
1812
  return bar - 1;
1722
1813
  }
1723
1814
  return bar;
1724
1815
  }
1725
1816
  function sliceBars(notes, meter, spanStart, spanEnd, budget) {
1726
- const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, spanStart, meter);
1817
+ const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, spanStart, meter);
1727
1818
  const lastBar = lastBarOf(meter, spanStart, spanEnd);
1728
1819
  const barCount = lastBar - firstBar + 1;
1729
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, barCount, "form bars", budget);
1820
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, barCount, "form bars", budget);
1730
1821
  const buckets = Array.from({ length: barCount }, () => []);
1731
1822
  let memberships = 0;
1732
1823
  for (const note of notes) {
1733
1824
  const noteEnd = note.startBeat + note.durationBeat;
1734
- const from = Math.max(firstBar, _chunkS5BKZZGMcjs.barIndexAt.call(void 0, note.startBeat, meter));
1735
- const to = Math.min(lastBar, _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(note.startBeat, noteEnd - _chunkS5BKZZGMcjs.BEAT_EPS), meter));
1825
+ const from = Math.max(firstBar, _chunkS3QELJUEcjs.barIndexAt.call(void 0, note.startBeat, meter));
1826
+ const to = Math.min(lastBar, _chunkS3QELJUEcjs.barIndexAt.call(void 0, Math.max(note.startBeat, noteEnd - _chunkS3QELJUEcjs.BEAT_EPS), meter));
1736
1827
  memberships += Math.max(0, to - from + 1);
1737
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, memberships, "form note-to-bar memberships", budget);
1828
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, memberships, "form note-to-bar memberships", budget);
1738
1829
  for (let bar = from; bar <= to; bar += 1) {
1739
1830
  _optionalChain([buckets, 'access', _8 => _8[bar - firstBar], 'optionalAccess', _9 => _9.push, 'call', _10 => _10(note)]);
1740
1831
  }
1741
1832
  }
1742
1833
  const slices = [];
1743
1834
  for (let bar = firstBar; bar <= lastBar; bar += 1) {
1744
- const startBeat = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
1745
- const endBeat = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: bar + 1, beat: 0 }, meter);
1835
+ const startBeat = _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
1836
+ const endBeat = _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar: bar + 1, beat: 0 }, meter);
1746
1837
  const barNotes = _nullishCoalesce(buckets[bar - firstBar], () => ( []));
1747
1838
  const { weights, totalWeight } = windowWeights(barNotes, startBeat, endBeat, meter);
1748
1839
  slices.push({ index: bar, startBeat, endBeat, notes: barNotes, weights, totalWeight });
@@ -1760,10 +1851,10 @@ function weightSimilarity(a, b) {
1760
1851
  normA += x * x;
1761
1852
  normB += y * y;
1762
1853
  }
1763
- if (normA <= _chunkS5BKZZGMcjs.BEAT_EPS && normB <= _chunkS5BKZZGMcjs.BEAT_EPS) {
1854
+ if (normA <= _chunkS3QELJUEcjs.BEAT_EPS && normB <= _chunkS3QELJUEcjs.BEAT_EPS) {
1764
1855
  return 1;
1765
1856
  }
1766
- if (normA <= _chunkS5BKZZGMcjs.BEAT_EPS || normB <= _chunkS5BKZZGMcjs.BEAT_EPS) {
1857
+ if (normA <= _chunkS3QELJUEcjs.BEAT_EPS || normB <= _chunkS3QELJUEcjs.BEAT_EPS) {
1767
1858
  return 0;
1768
1859
  }
1769
1860
  return clamp01(dot / Math.sqrt(normA * normB));
@@ -1790,13 +1881,23 @@ function combineEvidence(strengths) {
1790
1881
  // src/analyze/grid.ts
1791
1882
  function gridOriginOf(firstOnset, slotBeats) {
1792
1883
  const startBeat = firstOnset < 0 && firstOnset > -HUMANIZE_ADJACENCY ? 0 : firstOnset;
1793
- return { origin: Math.floor(startBeat / slotBeats + _chunkS5BKZZGMcjs.BEAT_EPS) * slotBeats, startBeat };
1884
+ return { origin: Math.floor(startBeat / slotBeats + _chunkS3QELJUEcjs.BEAT_EPS) * slotBeats, startBeat };
1794
1885
  }
1795
1886
  function gridForNotes(notes, slotBeats) {
1796
1887
  return gridOriginOf(firstSoundingBeat(notes), slotBeats);
1797
1888
  }
1889
+ function latestSoundingEnd(notes, fallback = 0) {
1890
+ if (notes.length === 0) {
1891
+ return fallback;
1892
+ }
1893
+ let end = Number.NEGATIVE_INFINITY;
1894
+ for (const note of notes) {
1895
+ end = Math.max(end, note.startBeat + note.durationBeat);
1896
+ }
1897
+ return end;
1898
+ }
1798
1899
  function barGridStart(firstOnset, meter) {
1799
- return gridOriginOf(firstOnset, _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, firstOnset, meter)).startBeat;
1900
+ return gridOriginOf(firstOnset, _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, firstOnset, meter)).startBeat;
1800
1901
  }
1801
1902
 
1802
1903
  // src/analyze/keys/index.ts
@@ -1816,13 +1917,13 @@ var FIFTHS_DISTANCE_COST = 0.18;
1816
1917
  var KEY_CANDIDATES = (() => {
1817
1918
  const candidates = [];
1818
1919
  for (let rootPc = 0; rootPc < 12; rootPc += 1) {
1819
- candidates.push({ key: _chunk3R3QVKY5cjs.majorKey.call(void 0, rootPc), rootPc, minor: false });
1820
- candidates.push({ key: _chunk3R3QVKY5cjs.minorKey.call(void 0, rootPc), rootPc, minor: true });
1920
+ candidates.push({ key: _chunkYDGMWH43cjs.majorKey.call(void 0, rootPc), rootPc, minor: false });
1921
+ candidates.push({ key: _chunkYDGMWH43cjs.minorKey.call(void 0, rootPc), rootPc, minor: true });
1821
1922
  }
1822
1923
  return candidates;
1823
1924
  })();
1824
1925
  function fifthsOf(rootPc, minor) {
1825
- const majorRoot = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, minor ? rootPc + 3 : rootPc);
1926
+ const majorRoot = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, minor ? rootPc + 3 : rootPc);
1826
1927
  const raw = majorRoot * 7 % 12;
1827
1928
  return raw > 6 ? raw - 12 : raw;
1828
1929
  }
@@ -1840,9 +1941,9 @@ function chooseKeys(slots, changeCost, meter, budget) {
1840
1941
  if (slots.length === 0) {
1841
1942
  return [];
1842
1943
  }
1843
- const back = _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slots.length, count, "key search choices", budget);
1844
- let costs = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
1845
- let spare = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
1944
+ const back = _chunkS3QELJUEcjs.allocateChoiceTable.call(void 0, slots.length, count, "key search choices", budget);
1945
+ let costs = _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
1946
+ let spare = _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
1846
1947
  const opening = _nullishCoalesce(_optionalChain([slots, 'access', _11 => _11[0], 'optionalAccess', _12 => _12.scores]), () => ( []));
1847
1948
  for (let c = 0; c < count; c += 1) {
1848
1949
  costs[c] = -(_nullishCoalesce(opening[c], () => ( 0)));
@@ -1851,7 +1952,7 @@ function chooseKeys(slots, changeCost, meter, budget) {
1851
1952
  const slot = slots[i];
1852
1953
  const previous = costs;
1853
1954
  const next = spare;
1854
- const accent = _chunkS5BKZZGMcjs.metricWeight.call(void 0, _nullishCoalesce(_optionalChain([slot, 'optionalAccess', _13 => _13.startBeat]), () => ( 0)), meter);
1955
+ const accent = _chunkS3QELJUEcjs.metricWeight.call(void 0, _nullishCoalesce(_optionalChain([slot, 'optionalAccess', _13 => _13.startBeat]), () => ( 0)), meter);
1855
1956
  const metricFactor = 1 - STRONG_BEAT_DISCOUNT * accent / MAX_METRIC_WEIGHT;
1856
1957
  const rowBase = i * count;
1857
1958
  for (let to = 0; to < count; to += 1) {
@@ -1921,7 +2022,7 @@ function enforceMinimumRegion(slots, path, minBeats, budget) {
1921
2022
  return chosen;
1922
2023
  }
1923
2024
  const candidates = KEY_CANDIDATES.length;
1924
- const totals = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, slots.length, candidates, "key region scores", budget);
2025
+ const totals = _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, slots.length, candidates, "key region scores", budget);
1925
2026
  for (let i = 0; i < slots.length; i += 1) {
1926
2027
  const scores = _nullishCoalesce(_optionalChain([slots, 'access', _18 => _18[i], 'optionalAccess', _19 => _19.scores]), () => ( []));
1927
2028
  const base = i * candidates;
@@ -1952,13 +2053,13 @@ function enforceMinimumRegion(slots, path, minBeats, budget) {
1952
2053
  return chosen;
1953
2054
  }
1954
2055
  const length = region.endBeat - region.startBeat;
1955
- if (region.merged || length >= minBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
2056
+ if (region.merged || length >= minBeats - _chunkS3QELJUEcjs.BEAT_EPS) {
1956
2057
  continue;
1957
2058
  }
1958
2059
  const previous = region.previous;
1959
2060
  const next = region.next;
1960
- const before = previous !== void 0 && Math.abs(previous.endBeat - region.startBeat) < _chunkS5BKZZGMcjs.BEAT_EPS ? previous : void 0;
1961
- const after = next !== void 0 && Math.abs(next.startBeat - region.endBeat) < _chunkS5BKZZGMcjs.BEAT_EPS ? next : void 0;
2061
+ const before = previous !== void 0 && Math.abs(previous.endBeat - region.startBeat) < _chunkS3QELJUEcjs.BEAT_EPS ? previous : void 0;
2062
+ const after = next !== void 0 && Math.abs(next.startBeat - region.endBeat) < _chunkS3QELJUEcjs.BEAT_EPS ? next : void 0;
1962
2063
  if (before === void 0 && after === void 0) {
1963
2064
  continue;
1964
2065
  }
@@ -2068,7 +2169,7 @@ function withRelations(regions) {
2068
2169
  if (previous === void 0 || current === void 0) {
2069
2170
  continue;
2070
2171
  }
2071
- const relation = _chunk3R3QVKY5cjs.keyRelationBetween.call(void 0, previous.key, current.key);
2172
+ const relation = _chunkYDGMWH43cjs.keyRelationBetween.call(void 0, previous.key, current.key);
2072
2173
  if (relation !== null && relation !== "same") {
2073
2174
  current.modulation = relation;
2074
2175
  }
@@ -2076,22 +2177,26 @@ function withRelations(regions) {
2076
2177
  return regions;
2077
2178
  }
2078
2179
  function keyTimelineFromNotes(notes, opts = {}) {
2079
- const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "key timeline meters");
2080
- const bar = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters);
2180
+ const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "key timeline meters");
2181
+ const bar = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, 0, meters);
2081
2182
  const expectedKeyBeats = _nullishCoalesce(opts.expectedKeyBeats, () => ( bar * 4));
2082
- _chunkS5BKZZGMcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
2183
+ _chunkS3QELJUEcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
2083
2184
  const minKeyBeats = _nullishCoalesce(opts.minKeyBeats, () => ( bar));
2084
- _chunkS5BKZZGMcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
2185
+ _chunkS3QELJUEcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
2085
2186
  const budget = opts.budget;
2086
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "key timeline notes", { allowNonPositiveDuration: true, budget });
2187
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "key timeline notes", { allowNonPositiveDuration: true, budget });
2087
2188
  const sounding = notes.filter((note) => note.durationBeat > 0);
2088
- const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
2089
- const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastEnd));
2090
- _chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "key timeline totalBeats");
2189
+ const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( latestSoundingEnd(sounding)));
2190
+ _chunkS3QELJUEcjs.assertRange.call(void 0,
2191
+ totalBeats,
2192
+ Math.min(0, firstSoundingBeat(sounding)),
2193
+ Number.MAX_SAFE_INTEGER,
2194
+ "key timeline totalBeats"
2195
+ );
2091
2196
  const slotBeats = Math.min(minKeyBeats, expectedKeyBeats);
2092
2197
  const { origin, startBeat: musicStart } = gridForNotes(sounding, slotBeats);
2093
- const slotCount = Math.max(0, Math.ceil((totalBeats - origin) / slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS));
2094
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slotCount * KEY_CANDIDATES.length, "key timeline slots", budget);
2198
+ const slotCount = Math.max(0, Math.ceil((totalBeats - origin) / slotBeats - _chunkS3QELJUEcjs.BEAT_EPS));
2199
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, slotCount * KEY_CANDIDATES.length, "key timeline slots", budget);
2095
2200
  if (slotCount === 0 || sounding.length === 0) {
2096
2201
  return [];
2097
2202
  }
@@ -2129,7 +2234,7 @@ function keyTimelineFromNotes(notes, opts = {}) {
2129
2234
  (candidate) => profileScore(weights, candidate.minor ? profile.minor : profile.major, candidate.rootPc) * totalWeight
2130
2235
  );
2131
2236
  slots.push({ startBeat, endBeat, scores, weight: totalWeight });
2132
- if (totalWeight > _chunkS5BKZZGMcjs.BEAT_EPS) {
2237
+ if (totalWeight > _chunkS3QELJUEcjs.BEAT_EPS) {
2133
2238
  weightSum += totalWeight;
2134
2239
  soundingSlots += 1;
2135
2240
  }
@@ -2143,14 +2248,14 @@ function keyTimelineFromNotes(notes, opts = {}) {
2143
2248
  const grouped = regionsFromPath(slots, path);
2144
2249
  const head = grouped[0];
2145
2250
  const next = grouped[1];
2146
- if (head !== void 0 && next !== void 0 && head.endBeat - Math.max(head.startBeat, musicStart) < slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
2251
+ if (head !== void 0 && next !== void 0 && head.endBeat - Math.max(head.startBeat, musicStart) < slotBeats - _chunkS3QELJUEcjs.BEAT_EPS) {
2147
2252
  next.startBeat = head.startBeat;
2148
2253
  next.from = head.from;
2149
2254
  grouped.shift();
2150
2255
  }
2151
2256
  const regions = grouped.map((region) => {
2152
2257
  const candidate = KEY_CANDIDATES[region.candidate];
2153
- const key = _chunk3R3QVKY5cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _24 => _24.key]), () => ( _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
2258
+ const key = _chunkYDGMWH43cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _24 => _24.key]), () => ( _chunkYDGMWH43cjs.majorKey.call(void 0, 0))));
2154
2259
  const startBeat = Math.max(region.startBeat, musicStart);
2155
2260
  const weights = new Array(PITCH_CLASSES).fill(0);
2156
2261
  for (let i = region.from; i < region.toExclusive; i += 1) {
@@ -2170,7 +2275,7 @@ function keyTimelineFromNotes(notes, opts = {}) {
2170
2275
  return {
2171
2276
  startBeat,
2172
2277
  endBeat: region.endBeat,
2173
- key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, key),
2278
+ key: _chunkYDGMWH43cjs.resolveKey.call(void 0, key),
2174
2279
  confidence: Math.max(0, correlation)
2175
2280
  };
2176
2281
  });
@@ -2182,20 +2287,20 @@ var TONIC_CREDIT = 0.4;
2182
2287
  var CADENCE_CREDIT = 0.6;
2183
2288
  var CHORD_MODULATION_COST = 0.6;
2184
2289
  function chordFitsKey(chord, candidate) {
2185
- const tones = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord);
2290
+ const tones = _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord);
2186
2291
  if (tones.length === 0) {
2187
2292
  return 0;
2188
2293
  }
2189
2294
  let inKey = 0;
2190
2295
  for (const pc of tones) {
2191
- if (_chunk3R3QVKY5cjs.isScaleTone.call(void 0, pc, candidate.key)) {
2296
+ if (_chunkYDGMWH43cjs.isScaleTone.call(void 0, pc, candidate.key)) {
2192
2297
  inKey += 1;
2193
2298
  }
2194
2299
  }
2195
2300
  let score = CHORD_TONE_CREDIT * ((inKey - (tones.length - inKey)) / tones.length);
2196
- const rootDegree = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - candidate.rootPc);
2301
+ const rootDegree = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - candidate.rootPc);
2197
2302
  if (rootDegree === 7) {
2198
- if (_chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, chord)) {
2303
+ if (_chunkYDGMWH43cjs.soundsDominantSeventh.call(void 0, chord)) {
2199
2304
  score += DOMINANT_CREDIT;
2200
2305
  } else if (chord.quality === "maj") {
2201
2306
  score += DOMINANT_CREDIT * 0.5;
@@ -2207,25 +2312,26 @@ function chordFitsKey(chord, candidate) {
2207
2312
  return score;
2208
2313
  }
2209
2314
  function detectModulations(chords, opts = {}) {
2210
- _chunkS5BKZZGMcjs.assertArray.call(void 0, chords, "modulation chord segments");
2315
+ _chunkS3QELJUEcjs.assertArray.call(void 0, chords, "modulation chord segments");
2211
2316
  chords.forEach((segment, index) => {
2212
- _chunkS5BKZZGMcjs.assertRecord.call(void 0, segment, `modulation chord segments[${index}]`);
2317
+ _chunkS3QELJUEcjs.assertRecord.call(void 0, segment, `modulation chord segments[${index}]`);
2213
2318
  });
2214
- const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "modulation meters");
2215
- const bar = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters);
2319
+ const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "modulation meters");
2320
+ const bar = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, 0, meters);
2216
2321
  const expectedKeyBeats = _nullishCoalesce(opts.expectedKeyBeats, () => ( bar * 4));
2217
- _chunkS5BKZZGMcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
2322
+ _chunkS3QELJUEcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
2218
2323
  const minKeyBeats = _nullishCoalesce(opts.minKeyBeats, () => ( bar));
2219
- _chunkS5BKZZGMcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
2220
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
2324
+ _chunkS3QELJUEcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
2325
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
2221
2326
  chords.length * KEY_CANDIDATES.length,
2222
2327
  "modulation chord segments",
2223
2328
  opts.budget
2224
2329
  );
2225
2330
  const sorted = [...chords].filter((segment) => segment.endBeat > segment.startBeat).sort((a, b) => a.startBeat - b.startBeat);
2226
- const lastChordEnd = sorted.reduce((end, segment) => Math.max(end, segment.endBeat), 0);
2331
+ const lastChordEnd = sorted.length === 0 ? 0 : sorted.reduce((end, segment) => Math.max(end, segment.endBeat), Number.NEGATIVE_INFINITY);
2227
2332
  const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastChordEnd));
2228
- _chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "modulation totalBeats");
2333
+ const lowest = totalBeats < 0 ? Math.min(0, _nullishCoalesce(_optionalChain([sorted, 'access', _27 => _27[0], 'optionalAccess', _28 => _28.startBeat]), () => ( 0))) : 0;
2334
+ _chunkS3QELJUEcjs.assertRange.call(void 0, totalBeats, lowest, Number.MAX_SAFE_INTEGER, "modulation totalBeats");
2229
2335
  const ordered = sorted.map((segment) => ({ ...segment, endBeat: Math.min(segment.endBeat, totalBeats) })).filter((segment) => segment.endBeat > segment.startBeat);
2230
2336
  if (ordered.length === 0) {
2231
2337
  return [];
@@ -2242,20 +2348,20 @@ function detectModulations(chords, opts = {}) {
2242
2348
  };
2243
2349
  });
2244
2350
  for (let i = 1; i < slots.length; i += 1) {
2245
- const arriving = _optionalChain([ordered, 'access', _27 => _27[i], 'optionalAccess', _28 => _28.chord]);
2246
- const approaching = _optionalChain([ordered, 'access', _29 => _29[i - 1], 'optionalAccess', _30 => _30.chord]);
2351
+ const arriving = _optionalChain([ordered, 'access', _29 => _29[i], 'optionalAccess', _30 => _30.chord]);
2352
+ const approaching = _optionalChain([ordered, 'access', _31 => _31[i - 1], 'optionalAccess', _32 => _32.chord]);
2247
2353
  const slot = slots[i];
2248
2354
  if (arriving === void 0 || approaching === void 0 || slot === void 0) {
2249
2355
  continue;
2250
2356
  }
2251
- const isDominantMotion = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, approaching.rootPc - arriving.rootPc) === 7 && _chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, approaching);
2357
+ const isDominantMotion = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, approaching.rootPc - arriving.rootPc) === 7 && _chunkYDGMWH43cjs.soundsDominantSeventh.call(void 0, approaching);
2252
2358
  if (!isDominantMotion) {
2253
2359
  continue;
2254
2360
  }
2255
2361
  const length = slot.endBeat - slot.startBeat;
2256
2362
  for (let c = 0; c < KEY_CANDIDATES.length; c += 1) {
2257
2363
  const candidate = KEY_CANDIDATES[c];
2258
- if (candidate !== void 0 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, arriving.rootPc - candidate.rootPc) === 0) {
2364
+ if (candidate !== void 0 && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, arriving.rootPc - candidate.rootPc) === 0) {
2259
2365
  slot.scores[c] = (_nullishCoalesce(slot.scores[c], () => ( 0))) + CADENCE_CREDIT * length;
2260
2366
  }
2261
2367
  }
@@ -2269,7 +2375,7 @@ function detectModulations(chords, opts = {}) {
2269
2375
  const profile = resolveKeyProfile(opts.profile);
2270
2376
  const regions = grouped.map((region) => {
2271
2377
  const candidate = KEY_CANDIDATES[region.candidate];
2272
- const key = _chunk3R3QVKY5cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _31 => _31.key]), () => ( _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
2378
+ const key = _chunkYDGMWH43cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _33 => _33.key]), () => ( _chunkYDGMWH43cjs.majorKey.call(void 0, 0))));
2273
2379
  const weights = new Array(PITCH_CLASSES).fill(0);
2274
2380
  for (let i = region.from; i < region.toExclusive; i += 1) {
2275
2381
  const segment = ordered[i];
@@ -2277,17 +2383,17 @@ function detectModulations(chords, opts = {}) {
2277
2383
  continue;
2278
2384
  }
2279
2385
  const overlap = segment.endBeat - segment.startBeat;
2280
- if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
2386
+ if (overlap <= _chunkS3QELJUEcjs.BEAT_EPS) {
2281
2387
  continue;
2282
2388
  }
2283
- for (const pc of _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, segment.chord)) {
2389
+ for (const pc of _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, segment.chord)) {
2284
2390
  weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + overlap;
2285
2391
  }
2286
2392
  }
2287
2393
  const correlation = profileScore(
2288
2394
  weights,
2289
- _optionalChain([candidate, 'optionalAccess', _32 => _32.minor]) ? profile.minor : profile.major,
2290
- _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _33 => _33.rootPc]), () => ( 0))
2395
+ _optionalChain([candidate, 'optionalAccess', _34 => _34.minor]) ? profile.minor : profile.major,
2396
+ _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _35 => _35.rootPc]), () => ( 0))
2291
2397
  );
2292
2398
  return {
2293
2399
  startBeat: region.startBeat,
@@ -2305,7 +2411,7 @@ function detectModulations(chords, opts = {}) {
2305
2411
  var THIRD_SEMITONES = [3, 4];
2306
2412
  var FIFTH_SEMITONES = [6, 7, 8];
2307
2413
  function triadCore(chord) {
2308
- const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc);
2414
+ const root = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc);
2309
2415
  let third;
2310
2416
  let fifth;
2311
2417
  for (const interval of chord.intervals) {
@@ -2321,10 +2427,10 @@ function triadCore(chord) {
2321
2427
  if (third === void 0 || fifth === void 0) {
2322
2428
  return null;
2323
2429
  }
2324
- return [root, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, root + third), _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, root + fifth)].sort((a, b) => a - b);
2430
+ return [root, _chunkS3QELJUEcjs.pitchClassOf.call(void 0, root + third), _chunkS3QELJUEcjs.pitchClassOf.call(void 0, root + fifth)].sort((a, b) => a - b);
2325
2431
  }
2326
2432
  function lastChordFinishedBy(chords) {
2327
- const byEnd = chords.map((_, index) => index).sort((a, b) => (_nullishCoalesce(_optionalChain([chords, 'access', _34 => _34[a], 'optionalAccess', _35 => _35.endBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([chords, 'access', _36 => _36[b], 'optionalAccess', _37 => _37.endBeat]), () => ( 0))));
2433
+ const byEnd = chords.map((_, index) => index).sort((a, b) => (_nullishCoalesce(_optionalChain([chords, 'access', _36 => _36[a], 'optionalAccess', _37 => _37.endBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([chords, 'access', _38 => _38[b], 'optionalAccess', _39 => _39.endBeat]), () => ( 0))));
2328
2434
  const latest = new Int32Array(byEnd.length);
2329
2435
  let running = 0;
2330
2436
  for (let i = 0; i < byEnd.length; i += 1) {
@@ -2336,7 +2442,7 @@ function lastChordFinishedBy(chords) {
2336
2442
  let high = byEnd.length;
2337
2443
  while (low < high) {
2338
2444
  const mid = low + high >> 1;
2339
- if ((_nullishCoalesce(_optionalChain([chords, 'access', _38 => _38[_nullishCoalesce(byEnd[mid], () => ( 0))], 'optionalAccess', _39 => _39.endBeat]), () => ( 0))) <= beat) {
2445
+ if ((_nullishCoalesce(_optionalChain([chords, 'access', _40 => _40[_nullishCoalesce(byEnd[mid], () => ( 0))], 'optionalAccess', _41 => _41.endBeat]), () => ( 0))) <= beat) {
2340
2446
  low = mid + 1;
2341
2447
  } else {
2342
2448
  high = mid;
@@ -2359,7 +2465,7 @@ function attachPivots(regions, chords) {
2359
2465
  if (previous === void 0 || current === void 0) {
2360
2466
  continue;
2361
2467
  }
2362
- const last = finished(current.startBeat + _chunkS5BKZZGMcjs.BEAT_EPS);
2468
+ const last = finished(current.startBeat + _chunkS3QELJUEcjs.BEAT_EPS);
2363
2469
  if (last === void 0) {
2364
2470
  continue;
2365
2471
  }
@@ -2367,9 +2473,9 @@ function attachPivots(regions, chords) {
2367
2473
  if (core === null) {
2368
2474
  continue;
2369
2475
  }
2370
- const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, last.chord.rootPc);
2476
+ const root = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, last.chord.rootPc);
2371
2477
  const pivot = pivotChords(previous.key, current.key).find(
2372
- (entry) => entry.chord.rootPc === root && sameTones(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, entry.chord), core)
2478
+ (entry) => entry.chord.rootPc === root && sameTones(_chunkYDGMWH43cjs.chordPitchClasses.call(void 0, entry.chord), core)
2373
2479
  );
2374
2480
  if (pivot !== void 0) {
2375
2481
  current.pivot = pivot;
@@ -2383,7 +2489,7 @@ function keyLookup(regions, fallback) {
2383
2489
  let high = regions.length;
2384
2490
  while (low < high) {
2385
2491
  const middle = Math.floor((low + high) / 2);
2386
- if ((_nullishCoalesce(_optionalChain([regions, 'access', _40 => _40[middle], 'optionalAccess', _41 => _41.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
2492
+ if ((_nullishCoalesce(_optionalChain([regions, 'access', _42 => _42[middle], 'optionalAccess', _43 => _43.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
2387
2493
  low = middle + 1;
2388
2494
  } else {
2389
2495
  high = middle;
@@ -2394,12 +2500,12 @@ function keyLookup(regions, fallback) {
2394
2500
  };
2395
2501
  }
2396
2502
  function prevailingKeyOf(regions) {
2397
- _chunkS5BKZZGMcjs.assertArray.call(void 0, regions, "key regions");
2503
+ _chunkS3QELJUEcjs.assertArray.call(void 0, regions, "key regions");
2398
2504
  const totals = /* @__PURE__ */ new Map();
2399
2505
  for (const [index, given] of regions.entries()) {
2400
- const region = _chunkS5BKZZGMcjs.assertRecord.call(void 0, given, `key regions[${index}]`);
2401
- _chunkS5BKZZGMcjs.assertRecord.call(void 0, region.key, `key regions[${index}].key`);
2402
- const id = `${_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, region.key.scale.rootPc)}:${region.key.scale.modeMask12}`;
2506
+ const region = _chunkS3QELJUEcjs.assertRecord.call(void 0, given, `key regions[${index}]`);
2507
+ _chunkS3QELJUEcjs.assertRecord.call(void 0, region.key, `key regions[${index}].key`);
2508
+ const id = `${_chunkS3QELJUEcjs.pitchClassOf.call(void 0, region.key.scale.rootPc)}:${region.key.scale.modeMask12}`;
2403
2509
  const entry = totals.get(id);
2404
2510
  const length = Math.max(0, region.endBeat - region.startBeat);
2405
2511
  if (entry === void 0) {
@@ -2422,13 +2528,13 @@ function prevailingKeyOf(regions) {
2422
2528
  // src/analyze/voice/index.ts
2423
2529
  function keyScaleAt(key) {
2424
2530
  if (typeof key === "function") {
2425
- return key;
2531
+ return (beat) => _chunkYDGMWH43cjs.toKeyScale.call(void 0, key(beat));
2426
2532
  }
2427
- const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
2533
+ const scale = _chunkYDGMWH43cjs.toKeyScale.call(void 0, key);
2428
2534
  return () => scale;
2429
2535
  }
2430
2536
  function toVoiceNotes(events) {
2431
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, "voice notes", { allowNonPositiveDuration: true });
2537
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, events, "voice notes", { allowNonPositiveDuration: true });
2432
2538
  return events.map((event, index) => ({ ...event, id: index, originalIndex: index }));
2433
2539
  }
2434
2540
  function intervalAboveBass(pitch, others, chord) {
@@ -2440,7 +2546,7 @@ function intervalAboveBass(pitch, others, chord) {
2440
2546
  return ((pitch - bass) % 12 + 12) % 12;
2441
2547
  }
2442
2548
  const ref = _nullishCoalesce(chord.bassPc, () => ( chord.rootPc));
2443
- return ((_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch) - _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, ref)) % 12 + 12) % 12;
2549
+ return ((_chunkS3QELJUEcjs.pitchClassOf.call(void 0, pitch) - _chunkS3QELJUEcjs.pitchClassOf.call(void 0, ref)) % 12 + 12) % 12;
2444
2550
  }
2445
2551
  var TENSION_DEGREE_BY_INTERVAL = {
2446
2552
  1: 9,
@@ -2451,7 +2557,8 @@ var TENSION_DEGREE_BY_INTERVAL = {
2451
2557
  8: 13,
2452
2558
  9: 13
2453
2559
  };
2454
- var NATURAL_TENSION_INTERVALS = /* @__PURE__ */ new Set([2, 5, 9]);
2560
+ var NATURAL_TENSION_INTERVAL_LIST = [2, 5, 9];
2561
+ var NATURAL_TENSION_INTERVALS = new Set(NATURAL_TENSION_INTERVAL_LIST);
2455
2562
  function tensionDegree(ic) {
2456
2563
  return TENSION_DEGREE_BY_INTERVAL[ic];
2457
2564
  }
@@ -2459,18 +2566,29 @@ function asSounded(chord) {
2459
2566
  if (chord.bassPc === void 0) {
2460
2567
  return chord;
2461
2568
  }
2462
- return { ...chord, intervals: [...chord.intervals, _chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, chord.bassPc, chord)] };
2569
+ return { ...chord, intervals: [...chord.intervals, _chunkYDGMWH43cjs.intervalAboveRoot.call(void 0, chord.bassPc, chord)] };
2463
2570
  }
2464
2571
  function isStep(a, b) {
2465
2572
  const d = Math.abs(a - b);
2466
2573
  return d === 1 || d === 2;
2467
2574
  }
2468
2575
  var LEAP_SEMITONES = 3;
2469
- function suspensionType(ic, delta) {
2576
+ function suspensionType(ic, delta, resolvedIc) {
2577
+ const figure = suspendedFigure(ic, delta);
2578
+ return figure !== null && SUSPENSION_RESOLUTIONS[figure].includes(resolvedIc) ? figure : null;
2579
+ }
2580
+ var SUSPENSION_RESOLUTIONS = {
2581
+ "sus4-3": [3, 4],
2582
+ "sus6-5": [7],
2583
+ "sus7-6": [8, 9],
2584
+ "sus9-8": [0],
2585
+ "sus2-3": [3, 4]
2586
+ };
2587
+ function suspendedFigure(ic, delta) {
2470
2588
  if (delta > 0) {
2471
2589
  return ic === 1 || ic === 2 ? "sus2-3" : null;
2472
2590
  }
2473
- if (ic === 2) {
2591
+ if (ic === 1 || ic === 2) {
2474
2592
  return "sus9-8";
2475
2593
  }
2476
2594
  if (ic === 10 || ic === 11) {
@@ -2484,43 +2602,58 @@ function suspensionType(ic, delta) {
2484
2602
  }
2485
2603
  return null;
2486
2604
  }
2605
+ function clashesWithChord(pitch, chord) {
2606
+ if (!_chunkS3QELJUEcjs.isConsonantInterval.call(void 0, intervalAboveBass(pitch, [], chord), true)) {
2607
+ return true;
2608
+ }
2609
+ const tones = chord.intervals.map((interval) => chord.rootPc + interval);
2610
+ if (chord.bassPc !== void 0) {
2611
+ tones.push(chord.bassPc);
2612
+ }
2613
+ return tones.some(
2614
+ (tone) => !_chunkS3QELJUEcjs.isConsonantInterval.call(void 0, ((_chunkS3QELJUEcjs.pitchClassOf.call(void 0, pitch) - tone) % 12 + 12) % 12, false)
2615
+ );
2616
+ }
2487
2617
  var STEP_RESOLUTION = 2;
2488
2618
  var NEAREST_CHORD_TONE_REACH = 12;
2489
2619
  function nearestChordTone(pitch, chord, maxDistance) {
2490
2620
  for (let delta = 1; delta <= maxDistance; delta += 1) {
2491
- if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, pitch - delta, chord)) {
2621
+ if (_chunkYDGMWH43cjs.isChordMember.call(void 0, pitch - delta, chord)) {
2492
2622
  return pitch - delta;
2493
2623
  }
2494
- if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, pitch + delta, chord)) {
2624
+ if (_chunkYDGMWH43cjs.isChordMember.call(void 0, pitch + delta, chord)) {
2495
2625
  return pitch + delta;
2496
2626
  }
2497
2627
  }
2498
2628
  return void 0;
2499
2629
  }
2500
2630
  function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2501
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, voice, "voice notes", { allowNonPositiveDuration: true });
2502
- _chunkS5BKZZGMcjs.assertFunction.call(void 0, chordAtBeat2, "chordAtBeat");
2503
- _chunkS5BKZZGMcjs.assertFunction.call(void 0, otherVoicesAtBeat, "otherVoicesAtBeat");
2631
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, voice, "voice notes", { allowNonPositiveDuration: true });
2632
+ _chunkS3QELJUEcjs.assertFunction.call(void 0, chordAtBeat2, "chordAtBeat");
2633
+ _chunkS3QELJUEcjs.assertFunction.call(void 0, otherVoicesAtBeat, "otherVoicesAtBeat");
2504
2634
  const keyAt = keyScaleAt(key);
2505
2635
  const result = [];
2636
+ const sounding = _chunkKMYGRFZ6cjs.soundingIndices.call(void 0, voice);
2637
+ const line = new Map(sounding.map((index, position) => [index, position]));
2506
2638
  for (let i = 0; i < voice.length; i += 1) {
2507
2639
  const note = voice[i];
2508
2640
  if (!note) {
2509
2641
  continue;
2510
2642
  }
2511
- const prevNote = i > 0 ? voice[i - 1] : void 0;
2512
- const nextNote = i + 1 < voice.length ? voice[i + 1] : void 0;
2513
- const prevEnd = prevNote === void 0 ? Number.NEGATIVE_INFINITY : prevNote.startBeat + prevNote.durationBeat;
2514
- const noteEnd = note.startBeat + note.durationBeat;
2515
- const prev = prevNote !== void 0 && adjacent(prevEnd, note.startBeat) ? prevNote : void 0;
2516
- const next = nextNote !== void 0 && adjacent(noteEnd, nextNote.startBeat) ? nextNote : void 0;
2643
+ const position = line.get(i);
2644
+ const prevIndex = position === void 0 ? void 0 : sounding[position - 1];
2645
+ const nextIndex = position === void 0 ? void 0 : sounding[position + 1];
2646
+ const prevNote = prevIndex === void 0 ? void 0 : voice[prevIndex];
2647
+ const nextNote = nextIndex === void 0 ? void 0 : voice[nextIndex];
2648
+ const prev = prevNote !== void 0 && !sameInstant(prevNote.startBeat, note.startBeat) && adjacent(prevNote.startBeat + prevNote.durationBeat, note.startBeat) ? prevNote : void 0;
2649
+ const next = nextNote !== void 0 && !sameInstant(nextNote.startBeat, note.startBeat) && adjacent(note.startBeat + note.durationBeat, nextNote.startBeat) ? nextNote : void 0;
2517
2650
  const chord = chordAtBeat2(note.startBeat);
2518
2651
  const labels = [];
2519
- const member = _chunk3R3QVKY5cjs.isChordMember.call(void 0, note.pitch, chord);
2652
+ const member = _chunkYDGMWH43cjs.isChordMember.call(void 0, note.pitch, chord);
2520
2653
  let handled = false;
2521
2654
  if (chord && member) {
2522
- const role = _chunk3R3QVKY5cjs.chordToneRole.call(void 0, note.pitch, chord);
2523
- const degree = role === null ? tensionDegree(_chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, note.pitch, chord)) : void 0;
2655
+ const role = _chunkYDGMWH43cjs.chordToneRole.call(void 0, note.pitch, chord);
2656
+ const degree = role === null ? tensionDegree(_chunkYDGMWH43cjs.intervalAboveRoot.call(void 0, note.pitch, chord)) : void 0;
2524
2657
  if (role) {
2525
2658
  labels.push({ kind: "chordTone", role });
2526
2659
  handled = true;
@@ -2528,7 +2661,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2528
2661
  labels.push({ kind: "tension", degree });
2529
2662
  handled = true;
2530
2663
  } else {
2531
- const bassRole = _chunk3R3QVKY5cjs.chordToneRole.call(void 0, note.pitch, asSounded(chord));
2664
+ const bassRole = _chunkYDGMWH43cjs.chordToneRole.call(void 0, note.pitch, asSounded(chord));
2532
2665
  if (bassRole !== null) {
2533
2666
  labels.push({ kind: "chordTone", role: bassRole });
2534
2667
  handled = true;
@@ -2537,15 +2670,14 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2537
2670
  }
2538
2671
  if (!handled && chord && prev && next) {
2539
2672
  const prevChord = chordAtBeat2(prev.startBeat);
2540
- const prepared = prev.pitch === note.pitch && _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, prevChord);
2673
+ const prepared = prev.pitch === note.pitch && _chunkYDGMWH43cjs.isChordMember.call(void 0, prev.pitch, prevChord);
2541
2674
  const resolves = isStep(next.pitch, note.pitch);
2542
2675
  const others = otherVoicesAtBeat(note.startBeat);
2543
- const verticallyDissonant = others.some(
2544
- (ov) => _chunk3R3QVKY5cjs.createsVerticalDissonance.call(void 0, note.pitch, ov.pitch, true)
2545
- );
2676
+ const verticallyDissonant = others.length > 0 ? others.some((ov) => _chunkYDGMWH43cjs.createsVerticalDissonance.call(void 0, note.pitch, ov.pitch, true)) : clashesWithChord(note.pitch, chord);
2546
2677
  if (prepared && verticallyDissonant && resolves) {
2547
2678
  const ic = intervalAboveBass(note.pitch, others, chord);
2548
- const type = suspensionType(ic, next.pitch - note.pitch);
2679
+ const resolvedIc = intervalAboveBass(next.pitch, others, chord);
2680
+ const type = suspensionType(ic, next.pitch - note.pitch, resolvedIc);
2549
2681
  if (type !== null) {
2550
2682
  labels.push({ kind: "suspension", type, resolveTo: next.pitch });
2551
2683
  handled = true;
@@ -2553,8 +2685,8 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2553
2685
  }
2554
2686
  }
2555
2687
  if (!handled && chord && prev && next) {
2556
- const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2557
- const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2688
+ const prevMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2689
+ const nextMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2558
2690
  const up = note.pitch - prev.pitch > 0;
2559
2691
  const contInto = next.pitch - note.pitch > 0;
2560
2692
  const sameDir = note.pitch !== prev.pitch && next.pitch !== note.pitch && up === contInto;
@@ -2563,20 +2695,20 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2563
2695
  handled = true;
2564
2696
  }
2565
2697
  }
2566
- if (!handled && chord && prev && next && !member && prev.pitch === next.pitch && _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat)) && isStep(note.pitch, prev.pitch)) {
2698
+ if (!handled && chord && prev && next && !member && prev.pitch === next.pitch && _chunkYDGMWH43cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat)) && isStep(note.pitch, prev.pitch)) {
2567
2699
  labels.push({ kind: "neighbor" });
2568
2700
  handled = true;
2569
2701
  }
2570
2702
  if (!handled && chord && next && !member && note.pitch === next.pitch) {
2571
2703
  const nextChord = chordAtBeat2(next.startBeat);
2572
- if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, nextChord)) {
2704
+ if (_chunkYDGMWH43cjs.isChordMember.call(void 0, next.pitch, nextChord)) {
2573
2705
  labels.push({ kind: "anticipation" });
2574
2706
  handled = true;
2575
2707
  }
2576
2708
  }
2577
2709
  if (!handled && chord && prev && next && !member) {
2578
- const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2579
- const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2710
+ const prevMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2711
+ const nextMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2580
2712
  const leapFromPrev = Math.abs(note.pitch - prev.pitch) >= LEAP_SEMITONES;
2581
2713
  const stepToNext = isStep(next.pitch, note.pitch);
2582
2714
  const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
@@ -2586,8 +2718,8 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2586
2718
  }
2587
2719
  }
2588
2720
  if (!handled && chord && prev && next && !member) {
2589
- const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2590
- const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2721
+ const prevMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
2722
+ const nextMember = _chunkYDGMWH43cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
2591
2723
  const stepFromPrev = isStep(note.pitch, prev.pitch);
2592
2724
  const leapToNext = Math.abs(next.pitch - note.pitch) >= LEAP_SEMITONES;
2593
2725
  const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
@@ -2597,7 +2729,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2597
2729
  }
2598
2730
  }
2599
2731
  if (!handled && chord && !member) {
2600
- const ic = _chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, note.pitch, chord);
2732
+ const ic = _chunkYDGMWH43cjs.intervalAboveRoot.call(void 0, note.pitch, chord);
2601
2733
  const degree = tensionDegree(ic);
2602
2734
  const colours = NATURAL_TENSION_INTERVALS.has(ic) || chord.intervals.some((i2) => i2 === 10 || i2 === 11);
2603
2735
  const avoid = ic === 5 && chord.intervals.includes(4) && !chord.intervals.includes(5) || ic === 11 && chord.intervals.includes(4) && chord.intervals.includes(10);
@@ -2616,7 +2748,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
2616
2748
  );
2617
2749
  }
2618
2750
  }
2619
- if (next && _chunk3R3QVKY5cjs.isLeadingToneResolution.call(void 0, note.pitch, next.pitch, keyAt(next.startBeat))) {
2751
+ if (next && _chunkYDGMWH43cjs.isLeadingToneResolution.call(void 0, note.pitch, next.pitch, keyAt(next.startBeat))) {
2620
2752
  labels.push({ kind: "leadingTone", resolveTo: next.pitch });
2621
2753
  }
2622
2754
  const analyzed = {
@@ -2667,23 +2799,49 @@ function describe(labels) {
2667
2799
 
2668
2800
  // src/analyze/timeline/index.ts
2669
2801
  function assertChordTimeline(timeline, name = "timeline") {
2670
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, timeline, name);
2671
- _chunkS5BKZZGMcjs.assertFunction.call(void 0, read.at, `${name}.at`);
2672
- const segments = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.segments, `${name}.segments`);
2802
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, timeline, name);
2803
+ _chunkS3QELJUEcjs.assertFunction.call(void 0, read.at, `${name}.at`);
2804
+ const segments = _chunkS3QELJUEcjs.assertArray.call(void 0, read.segments, `${name}.segments`);
2673
2805
  for (let index = 0; index < segments.length; index += 1) {
2674
- const segment = _chunkS5BKZZGMcjs.assertRecord.call(void 0, segments[index], `${name}.segments[${index}]`);
2675
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, segment.startBeat, `${name}.segments[${index}].startBeat`);
2676
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, segment.endBeat, `${name}.segments[${index}].endBeat`);
2806
+ const segment = _chunkS3QELJUEcjs.assertRecord.call(void 0, segments[index], `${name}.segments[${index}]`);
2807
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, segment.startBeat, `${name}.segments[${index}].startBeat`);
2808
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, segment.endBeat, `${name}.segments[${index}].endBeat`);
2677
2809
  }
2810
+ assertSpansInOrder(segments, `${name}.segments`);
2678
2811
  return read;
2679
2812
  }
2813
+ function assertSpansInOrder(spans, name) {
2814
+ for (let index = 0; index < spans.length; index += 1) {
2815
+ const span = spans[index];
2816
+ if (span === void 0) {
2817
+ continue;
2818
+ }
2819
+ if (!(span.endBeat > span.startBeat)) {
2820
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
2821
+ `${name}[${index}] must end after it starts; received [${span.startBeat}, ${span.endBeat})`
2822
+ );
2823
+ }
2824
+ const previous = spans[index - 1];
2825
+ if (previous !== void 0 && span.startBeat < previous.endBeat) {
2826
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
2827
+ `${name}[${index}] must not start before ${name}[${index - 1}] ends; received ${span.startBeat} before ${previous.endBeat}`
2828
+ );
2829
+ }
2830
+ }
2831
+ }
2832
+ function statedKeyRegions(key, startBeat, endBeat) {
2833
+ if (!(endBeat > startBeat)) {
2834
+ return [];
2835
+ }
2836
+ return [{ startBeat, endBeat, key, confidence: 1 }];
2837
+ }
2680
2838
  function chordTimelineFromChords(chords, totalBeats) {
2681
- _chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
2682
- _chunkS5BKZZGMcjs.assertArray.call(void 0, chords, "chords");
2683
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, chords.length, "timeline chords");
2839
+ _chunkS3QELJUEcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
2840
+ _chunkS3QELJUEcjs.assertArray.call(void 0, chords, "chords");
2841
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, chords.length, "timeline chords");
2684
2842
  for (let index = 0; index < chords.length; index += 1) {
2685
- _chunkS5BKZZGMcjs.assertRange.call(void 0,
2686
- _nullishCoalesce(_optionalChain([chords, 'access', _42 => _42[index], 'optionalAccess', _43 => _43.startBeat]), () => ( Number.NaN)),
2843
+ _chunkS3QELJUEcjs.assertRange.call(void 0,
2844
+ _nullishCoalesce(_optionalChain([chords, 'access', _44 => _44[index], 'optionalAccess', _45 => _45.startBeat]), () => ( Number.NaN)),
2687
2845
  -Number.MAX_SAFE_INTEGER,
2688
2846
  Number.MAX_SAFE_INTEGER,
2689
2847
  `chords[${index}].startBeat`
@@ -2697,13 +2855,13 @@ function chordTimelineFromChords(chords, totalBeats) {
2697
2855
  return {
2698
2856
  startBeat: gc.startBeat,
2699
2857
  endBeat,
2700
- chord: _chunk3R3QVKY5cjs.chordFromSpan.call(void 0, gc)
2858
+ chord: _chunkYDGMWH43cjs.chordFromSpan.call(void 0, gc)
2701
2859
  };
2702
2860
  }).filter((seg) => seg.endBeat > seg.startBeat);
2703
2861
  return { at: segmentLookup(segments), segments };
2704
2862
  }
2705
2863
  function chordAtBeat(segments, beat) {
2706
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "timeline query beat");
2864
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, beat, "timeline query beat");
2707
2865
  const candidate = segments[firstAfter(segments, beat) - 1];
2708
2866
  return candidate !== void 0 && beat >= candidate.startBeat && beat < candidate.endBeat ? candidate.chord : null;
2709
2867
  }
@@ -2712,7 +2870,7 @@ function firstAfter(segments, beat) {
2712
2870
  let high = segments.length;
2713
2871
  while (low < high) {
2714
2872
  const middle = Math.floor((low + high) / 2);
2715
- if ((_nullishCoalesce(_optionalChain([segments, 'access', _44 => _44[middle], 'optionalAccess', _45 => _45.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
2873
+ if ((_nullishCoalesce(_optionalChain([segments, 'access', _46 => _46[middle], 'optionalAccess', _47 => _47.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
2716
2874
  low = middle + 1;
2717
2875
  } else {
2718
2876
  high = middle;
@@ -2723,7 +2881,7 @@ function firstAfter(segments, beat) {
2723
2881
  function segmentStartingAt(segments, beat) {
2724
2882
  const index = firstAfter(segments, beat);
2725
2883
  for (const candidate of [segments[index - 1], segments[index]]) {
2726
- if (candidate !== void 0 && Math.abs(candidate.startBeat - beat) < _chunkS5BKZZGMcjs.BEAT_EPS) {
2884
+ if (candidate !== void 0 && Math.abs(candidate.startBeat - beat) < _chunkS3QELJUEcjs.BEAT_EPS) {
2727
2885
  return candidate;
2728
2886
  }
2729
2887
  }
@@ -2741,13 +2899,14 @@ var EXTRA_TONE_PENALTY = 0.6;
2741
2899
  var OMITTED_TONE_PENALTY = 0.15;
2742
2900
  var BASS_ROOT_STANDING = 0.95;
2743
2901
  var OCTAVE = 12;
2902
+ var CHORD_SEGMENTATIONS = ["dynamic", "grid"];
2744
2903
  function scoreMatch(match, weights, maxWeight, key, pedalPc) {
2745
2904
  const rootWeight = maxWeight > 0 ? (_nullishCoalesce(weights[match.rootPc], () => ( 0))) / maxWeight : 0;
2746
2905
  const rootStanding = match.inversion === 0 ? Math.max(rootWeight, BASS_ROOT_STANDING) : rootWeight;
2747
- const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, match.rootPc, match.quality);
2906
+ const chord = _chunkYDGMWH43cjs.makeChord.call(void 0, match.rootPc, match.quality);
2748
2907
  const account = matchAccount(match, chord, pedalPc);
2749
2908
  let score = rootStanding;
2750
- if (_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunk3R3QVKY5cjs.isScaleTone.call(void 0, pc, key))) {
2909
+ if (_chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunkYDGMWH43cjs.isScaleTone.call(void 0, pc, key))) {
2751
2910
  score += DIATONIC_BONUS;
2752
2911
  }
2753
2912
  if (account.exact) {
@@ -2757,12 +2916,15 @@ function scoreMatch(match, weights, maxWeight, key, pedalPc) {
2757
2916
  score -= OMITTED_TONE_PENALTY * account.missing;
2758
2917
  return score;
2759
2918
  }
2760
- var SLASH_UPPER_QUALITIES = /* @__PURE__ */ new Set(["maj", "min"]);
2919
+ var SLASH_UPPER_QUALITY_LIST = ["maj", "min"];
2920
+ var SLASH_UPPER_QUALITIES = new Set(
2921
+ SLASH_UPPER_QUALITY_LIST
2922
+ );
2761
2923
  function isSlashReading(match, pedalPc) {
2762
2924
  return pedalPc !== null && match.inversion === null && match.missingPcs.length === 0 && match.extraPcs.length === 1 && match.extraPcs[0] === pedalPc && SLASH_UPPER_QUALITIES.has(match.quality);
2763
2925
  }
2764
2926
  function namesAsExtension(chord, pc) {
2765
- return chord.intervals.some((iv) => iv >= OCTAVE && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === pc);
2927
+ return chord.intervals.some((iv) => iv >= OCTAVE && _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === pc);
2766
2928
  }
2767
2929
  function matchAccount(match, chord, pedalPc) {
2768
2930
  const unvoiced = pedalPc !== null && namesAsExtension(chord, pedalPc) ? 1 : 0;
@@ -2778,10 +2940,10 @@ function bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) {
2778
2940
  let highestBass = Number.NEGATIVE_INFINITY;
2779
2941
  for (const note of notes) {
2780
2942
  const overlap = Math.min(note.startBeat + note.durationBeat, windowEnd) - Math.max(note.startBeat, windowStart);
2781
- if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
2943
+ if (overlap <= _chunkS3QELJUEcjs.BEAT_EPS) {
2782
2944
  continue;
2783
2945
  }
2784
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, note.pitch) === bassPc) {
2946
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, note.pitch) === bassPc) {
2785
2947
  highestBass = Math.max(highestBass, note.pitch);
2786
2948
  } else {
2787
2949
  upperFloor = Math.min(upperFloor, note.pitch);
@@ -2789,8 +2951,32 @@ function bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) {
2789
2951
  }
2790
2952
  return highestBass <= upperFloor;
2791
2953
  }
2954
+ function windowBass(notes, windowStart, windowEnd, meters, harmonicRhythm) {
2955
+ const counted = [];
2956
+ let firstSounding = Number.POSITIVE_INFINITY;
2957
+ for (const note of notes) {
2958
+ const overlap = Math.min(note.startBeat + note.durationBeat, windowEnd) - Math.max(note.startBeat, windowStart);
2959
+ if (overlap <= _chunkS3QELJUEcjs.BEAT_EPS) {
2960
+ continue;
2961
+ }
2962
+ const onsetInWindow = note.startBeat >= windowStart - _chunkS3QELJUEcjs.BEAT_EPS && note.startBeat < windowEnd - _chunkS3QELJUEcjs.BEAT_EPS;
2963
+ if (noteWeight(note, overlap, onsetInWindow ? meters : void 0) <= _chunkS3QELJUEcjs.BEAT_EPS) {
2964
+ continue;
2965
+ }
2966
+ counted.push({ note, overlap });
2967
+ firstSounding = Math.min(firstSounding, Math.max(note.startBeat, windowStart));
2968
+ }
2969
+ const half = (windowEnd - windowStart) / 2;
2970
+ let bass = Number.POSITIVE_INFINITY;
2971
+ for (const { note, overlap } of counted) {
2972
+ if (note.startBeat <= firstSounding + _chunkS3QELJUEcjs.BEAT_EPS || overlap > half || overlap >= harmonicRhythm - _chunkS3QELJUEcjs.BEAT_EPS) {
2973
+ bass = Math.min(bass, note.pitch);
2974
+ }
2975
+ }
2976
+ return bass;
2977
+ }
2792
2978
  function chordConfidence(chord, weights, totalWeight, exact) {
2793
- const tones = new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord));
2979
+ const tones = new Set(_chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord));
2794
2980
  let chordWeight = 0;
2795
2981
  for (let pc = 0; pc < 12; pc += 1) {
2796
2982
  if (tones.has(pc)) {
@@ -2801,43 +2987,56 @@ function chordConfidence(chord, weights, totalWeight, exact) {
2801
2987
  const confidence = Math.min(1, Math.max(0, raw));
2802
2988
  return exact ? confidence : confidence * INEXACT_CONFIDENCE_FACTOR;
2803
2989
  }
2804
- function analyzeWindow(notes, windowStart, windowEnd, meters, key) {
2805
- const { weights, totalWeight, maxWeight, lowestPitch } = windowWeights(
2806
- notes,
2807
- windowStart,
2808
- windowEnd,
2809
- meters
2810
- );
2811
- if (totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
2990
+ function analyzeWindow(notes, windowStart, windowEnd, meters, key, harmonicRhythm) {
2991
+ const { weights, totalWeight, maxWeight } = windowWeights(notes, windowStart, windowEnd, meters);
2992
+ if (totalWeight <= _chunkS3QELJUEcjs.BEAT_EPS) {
2812
2993
  return null;
2813
2994
  }
2814
2995
  const ranked = weights.map((weight, pc) => ({ pc, weight })).filter(({ weight }) => weight > 0).sort((a, b) => b.weight - a.weight || a.pc - b.pc);
2815
- const selected = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
2816
- const bassPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, lowestPitch);
2817
- const hasBass = selected.includes(bassPc);
2818
- const detectionPitches = hasBass ? selected.map((pc) => pc === bassPc ? pc : pc + 12) : selected;
2819
- const pedalPc = hasBass && bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) ? bassPc : null;
2820
- const matches = detectChord(detectionPitches, { input: hasBass ? "midi" : "pitchClass" });
2996
+ const bassPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, windowBass(notes, windowStart, windowEnd, meters, harmonicRhythm));
2997
+ const sounding = ranked.slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
2998
+ const significant = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
2999
+ let detection = detectWindow(significant, bassPc, notes, windowStart, windowEnd);
3000
+ if (detection.matches.length === 0 && significant.length < sounding.length) {
3001
+ detection = detectWindow(sounding, bassPc, notes, windowStart, windowEnd);
3002
+ }
3003
+ const { selected, hasBass, pedalPc, matches } = detection;
2821
3004
  let bestMatch = null;
2822
3005
  let bestScore = Number.NEGATIVE_INFINITY;
2823
3006
  for (const match of matches) {
2824
- const score = scoreMatch(match, weights, maxWeight, _chunk3R3QVKY5cjs.scaleOf.call(void 0, key), pedalPc);
3007
+ const score = scoreMatch(match, weights, maxWeight, _chunkYDGMWH43cjs.scaleOf.call(void 0, key), pedalPc);
2825
3008
  if (score > bestScore) {
2826
3009
  bestScore = score;
2827
3010
  bestMatch = match;
2828
3011
  }
2829
3012
  }
2830
3013
  if (bestMatch) {
2831
- const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality, bestMatch.bassPc);
3014
+ const chord = _chunkYDGMWH43cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality, bestMatch.bassPc);
2832
3015
  const account = matchAccount(
2833
3016
  bestMatch,
2834
- _chunk3R3QVKY5cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality),
3017
+ _chunkYDGMWH43cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality),
2835
3018
  pedalPc
2836
3019
  );
3020
+ const explained = account.exact || isSlashReading(bestMatch, pedalPc);
2837
3021
  const reading = {
2838
3022
  chord: shared(chord),
2839
- confidence: chordConfidence(chord, weights, totalWeight, account.exact)
3023
+ confidence: chordConfidence(chord, weights, totalWeight, explained)
2840
3024
  };
3025
+ if (bestMatch.quality === SYMMETRIC_QUALITY) {
3026
+ reading.rootings = _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, _chunkYDGMWH43cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality)).map(
3027
+ (rootPc) => {
3028
+ const rooted = _chunkYDGMWH43cjs.makeChord.call(void 0,
3029
+ rootPc,
3030
+ bestMatch.quality,
3031
+ hasBass && bassPc !== rootPc ? bassPc : void 0
3032
+ );
3033
+ return {
3034
+ chord: shared(rooted),
3035
+ confidence: chordConfidence(rooted, weights, totalWeight, explained)
3036
+ };
3037
+ }
3038
+ );
3039
+ }
2841
3040
  const augmented = hasBass ? augmentedSixthFromPitchClasses(selected, bassPc, key) : null;
2842
3041
  if (augmented !== null) {
2843
3042
  reading.augmented = {
@@ -2849,14 +3048,21 @@ function analyzeWindow(notes, windowStart, windowEnd, meters, key) {
2849
3048
  }
2850
3049
  return null;
2851
3050
  }
3051
+ function detectWindow(selected, bassPc, notes, windowStart, windowEnd) {
3052
+ const hasBass = selected.includes(bassPc);
3053
+ const detectionPitches = hasBass ? selected.map((pc) => pc === bassPc ? pc : pc + 12) : selected;
3054
+ const pedalPc = hasBass && bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) ? bassPc : null;
3055
+ const matches = detectChord(detectionPitches, { input: hasBass ? "midi" : "pitchClass" });
3056
+ return { selected, hasBass, pedalPc, matches };
3057
+ }
2852
3058
  function shared(chord) {
2853
3059
  Object.freeze(chord.intervals);
2854
3060
  return Object.freeze(chord);
2855
3061
  }
2856
3062
  var DOMINANT_ABOVE_TONIC = 7;
2857
3063
  function resolvesOntoDominant(readings, index, key) {
2858
- const dominantPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + DOMINANT_ABOVE_TONIC);
2859
- const candidate = _optionalChain([readings, 'access', _46 => _46[index], 'optionalAccess', _47 => _47.chord]);
3064
+ const dominantPc = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc + DOMINANT_ABOVE_TONIC);
3065
+ const candidate = _optionalChain([readings, 'access', _48 => _48[index], 'optionalAccess', _49 => _49.chord]);
2860
3066
  for (let i = index + 1; i < readings.length; i += 1) {
2861
3067
  const next = readings[i];
2862
3068
  if (!next) {
@@ -2866,15 +3072,38 @@ function resolvesOntoDominant(readings, index, key) {
2866
3072
  if (candidate !== void 0 && sameChord(chord, candidate)) {
2867
3073
  continue;
2868
3074
  }
2869
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== dominantPc) {
3075
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== dominantPc) {
2870
3076
  return false;
2871
3077
  }
2872
- if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc) === dominantPc) {
3078
+ if (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc) === dominantPc) {
2873
3079
  return true;
2874
3080
  }
2875
3081
  }
2876
3082
  return true;
2877
3083
  }
3084
+ var SYMMETRIC_QUALITY = "dim7";
3085
+ function settledDiminishedRoot(readings, index, key) {
3086
+ const reading = readings[index];
3087
+ const rootings = _optionalChain([reading, 'optionalAccess', _50 => _50.rootings]);
3088
+ if (!reading || rootings === void 0) {
3089
+ return null;
3090
+ }
3091
+ const roots = rootings.map((rooting) => rooting.chord.rootPc);
3092
+ const tones = _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, reading.chord).join();
3093
+ for (let i = index + 1; i < readings.length; i += 1) {
3094
+ const next = readings[i];
3095
+ if (!next || _chunkYDGMWH43cjs.chordPitchClasses.call(void 0, next.chord).join() === tones) {
3096
+ continue;
3097
+ }
3098
+ const leading = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, next.chord.rootPc - 1);
3099
+ if (roots.includes(leading)) {
3100
+ return leading;
3101
+ }
3102
+ break;
3103
+ }
3104
+ const leadingTone = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc - 1);
3105
+ return roots.includes(leadingTone) ? leadingTone : null;
3106
+ }
2878
3107
  function sameChord(a, b) {
2879
3108
  return a.rootPc === b.rootPc && a.quality === b.quality && a.bassPc === b.bassPc;
2880
3109
  }
@@ -2902,7 +3131,7 @@ var LEXICON_TONES = (() => {
2902
3131
  for (const quality of SEGMENTATION_QUALITIES) {
2903
3132
  LEXICON_ROOTS[candidate] = rootPc;
2904
3133
  LEXICON_TONE_START[candidate] = tones.length;
2905
- tones.push(..._chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, quality)));
3134
+ tones.push(..._chunkYDGMWH43cjs.chordPitchClasses.call(void 0, _chunkYDGMWH43cjs.makeChord.call(void 0, rootPc, quality)));
2906
3135
  candidate += 1;
2907
3136
  }
2908
3137
  }
@@ -2923,7 +3152,7 @@ function candidateScore(candidate, weights, totalWeight) {
2923
3152
  for (let index = first; index < last; index += 1) {
2924
3153
  const weight = _nullishCoalesce(weights[_nullishCoalesce(LEXICON_TONES[index], () => ( 0))], () => ( 0));
2925
3154
  covered += weight;
2926
- if (weight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
3155
+ if (weight <= _chunkS3QELJUEcjs.BEAT_EPS) {
2927
3156
  missing += 1;
2928
3157
  }
2929
3158
  }
@@ -2939,16 +3168,16 @@ function slotOfBeat(grid, beat) {
2939
3168
  }
2940
3169
  function createBoundaryTables(slotCount, budget) {
2941
3170
  return {
2942
- scores: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window scores", budget),
2943
- back: _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window choices", budget),
2944
- costs: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
2945
- spare: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
2946
- path: _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slotCount, 1, "timeline window path", budget)
3171
+ scores: _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window scores", budget),
3172
+ back: _chunkS3QELJUEcjs.allocateChoiceTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window choices", budget),
3173
+ costs: _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
3174
+ spare: _chunkS3QELJUEcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
3175
+ path: _chunkS3QELJUEcjs.allocateChoiceTable.call(void 0, slotCount, 1, "timeline window path", budget)
2947
3176
  };
2948
3177
  }
2949
3178
  function scoreSlot(tables, slot, index) {
2950
3179
  const base = index * LEXICON_SIZE;
2951
- if (slot.totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
3180
+ if (slot.totalWeight <= _chunkS3QELJUEcjs.BEAT_EPS) {
2952
3181
  tables.scores.fill(0, base, base + LEXICON_SIZE);
2953
3182
  return;
2954
3183
  }
@@ -2978,7 +3207,7 @@ function chooseBoundaries(tables, from, to, grid, meters, changeCost) {
2978
3207
  }
2979
3208
  }
2980
3209
  const slotStart = beatOfSlot(grid, from + i);
2981
- const accent = _chunkS5BKZZGMcjs.metricWeight.call(void 0, slotStart, meters);
3210
+ const accent = _chunkS3QELJUEcjs.metricWeight.call(void 0, slotStart, meters);
2982
3211
  const penalty = changeCost * (1 - STRONG_BEAT_DISCOUNT2 * accent / MAX_METRIC_WEIGHT2);
2983
3212
  const rowBase = (from + i) * LEXICON_SIZE;
2984
3213
  const change = cheapest + penalty;
@@ -3020,7 +3249,7 @@ function chooseBoundaries(tables, from, to, grid, meters, changeCost) {
3020
3249
  }
3021
3250
  function slotBeatsWithin(gridUnit, minChordBeats, harmonicRhythm) {
3022
3251
  const resolution = Math.min(minChordBeats, harmonicRhythm);
3023
- const divisions = Math.max(1, Math.ceil(gridUnit / resolution - _chunkS5BKZZGMcjs.BEAT_EPS));
3252
+ const divisions = Math.max(1, Math.ceil(gridUnit / resolution - _chunkS3QELJUEcjs.BEAT_EPS));
3024
3253
  return gridUnit / divisions;
3025
3254
  }
3026
3255
  function gridSpans(slotCount, grid) {
@@ -3035,8 +3264,8 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
3035
3264
  let soundingSlots = 0;
3036
3265
  let weightSum = 0;
3037
3266
  for (let i = 0; i < slotCount; i += 1) {
3038
- const weight = _nullishCoalesce(_optionalChain([slotWeights, 'access', _48 => _48[i], 'optionalAccess', _49 => _49.totalWeight]), () => ( 0));
3039
- if (weight > _chunkS5BKZZGMcjs.BEAT_EPS) {
3267
+ const weight = _nullishCoalesce(_optionalChain([slotWeights, 'access', _51 => _51[i], 'optionalAccess', _52 => _52.totalWeight]), () => ( 0));
3268
+ if (weight > _chunkS3QELJUEcjs.BEAT_EPS) {
3040
3269
  soundingSlots += 1;
3041
3270
  weightSum += weight;
3042
3271
  }
@@ -3046,7 +3275,7 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
3046
3275
  }
3047
3276
  const meanSlotWeight = weightSum / soundingSlots;
3048
3277
  const changeCost = CHANGE_COST * meanSlotWeight * (harmonicRhythm / slotBeats);
3049
- const silent = slotWeights.map((slot) => slot.totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS);
3278
+ const silent = slotWeights.map((slot) => slot.totalWeight <= _chunkS3QELJUEcjs.BEAT_EPS);
3050
3279
  const breaks = new Array(slotCount).fill(false);
3051
3280
  for (let i = 0; i < slotCount; ) {
3052
3281
  if (!silent[i]) {
@@ -3057,7 +3286,7 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
3057
3286
  while (end < slotCount && silent[end]) {
3058
3287
  end += 1;
3059
3288
  }
3060
- if ((end - i) * slotBeats >= harmonicRhythm - _chunkS5BKZZGMcjs.BEAT_EPS) {
3289
+ if ((end - i) * slotBeats >= harmonicRhythm - _chunkS3QELJUEcjs.BEAT_EPS) {
3061
3290
  for (let k = i; k < end; k += 1) {
3062
3291
  breaks[k] = true;
3063
3292
  }
@@ -3120,45 +3349,46 @@ function evidenceFits(previous, grid, slotCount, totalBeats, segmentation, harmo
3120
3349
  return previous.grid.origin === grid.origin && previous.grid.slotBeats === grid.slotBeats && previous.slotCount === slotCount && previous.totalBeats === totalBeats && previous.segmentation === segmentation && previous.harmonicRhythm === harmonicRhythm && previous.keyGiven === keyGiven;
3121
3350
  }
3122
3351
  function analyzeTimeline(notes, opts = {}, previous, dirty) {
3123
- const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "timeline meters");
3124
- const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => ( _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters)));
3125
- _chunkS5BKZZGMcjs.assertRange.call(void 0, harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
3126
- const segmentation = _nullishCoalesce(opts.segmentation, () => ( "dynamic"));
3127
- const gridUnit = _chunkS5BKZZGMcjs.metricGridUnit.call(void 0, meters);
3352
+ const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "timeline meters");
3353
+ const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => ( _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, 0, meters)));
3354
+ _chunkS3QELJUEcjs.assertRange.call(void 0, harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
3355
+ const segmentation = _chunkS3QELJUEcjs.assertOneOf.call(void 0,
3356
+ _nullishCoalesce(opts.segmentation, () => ( "dynamic")),
3357
+ CHORD_SEGMENTATIONS,
3358
+ "timeline segmentation"
3359
+ );
3360
+ const gridUnit = _chunkS3QELJUEcjs.metricGridUnit.call(void 0, meters);
3128
3361
  const minChordBeats = _nullishCoalesce(opts.minChordBeats, () => ( gridUnit));
3129
- _chunkS5BKZZGMcjs.assertRange.call(void 0, minChordBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minChordBeats");
3362
+ _chunkS3QELJUEcjs.assertRange.call(void 0, minChordBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minChordBeats");
3130
3363
  const budget = opts.budget;
3131
3364
  const noteOptions = { allowNonPositiveDuration: true, budget };
3132
3365
  if (opts.pickupBeats !== void 0) {
3133
- _chunkS5BKZZGMcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "timeline pickupBeats");
3366
+ _chunkS3QELJUEcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "timeline pickupBeats");
3134
3367
  noteOptions.minStartBeat = -opts.pickupBeats;
3135
3368
  }
3136
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "timeline notes", noteOptions);
3137
- const sounding = _chunk35NBV2FTcjs.sortedNoteEvents.call(void 0, notes.filter((note) => note.durationBeat > 0));
3138
- const lastNoteEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
3139
- const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastNoteEnd));
3140
- _chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
3369
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "timeline notes", noteOptions);
3370
+ const sounding = _chunkKMYGRFZ6cjs.sortedNoteEvents.call(void 0, notes.filter((note) => note.durationBeat > 0));
3371
+ const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( latestSoundingEnd(sounding)));
3372
+ _chunkS3QELJUEcjs.assertRange.call(void 0,
3373
+ totalBeats,
3374
+ Math.min(0, firstSoundingBeat(sounding)),
3375
+ Number.MAX_SAFE_INTEGER,
3376
+ "timeline totalBeats"
3377
+ );
3141
3378
  const slotBeats = segmentation === "grid" ? harmonicRhythm : slotBeatsWithin(gridUnit, minChordBeats, harmonicRhythm);
3142
3379
  const { origin, startBeat: musicStart } = gridForNotes(sounding, slotBeats);
3143
3380
  const grid = { origin, slotBeats };
3144
- const slotCount = Math.max(0, Math.ceil((totalBeats - grid.origin) / slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS));
3381
+ const slotCount = Math.max(0, Math.ceil((totalBeats - grid.origin) / slotBeats - _chunkS3QELJUEcjs.BEAT_EPS));
3145
3382
  const dynamic = segmentation === "dynamic";
3146
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
3383
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
3147
3384
  dynamic ? slotCount * LEXICON_SIZE : slotCount,
3148
3385
  "timeline windows",
3149
3386
  budget
3150
3387
  );
3151
- const keys = opts.key !== void 0 ? [
3152
- {
3153
- startBeat: musicStart,
3154
- endBeat: totalBeats,
3155
- key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, opts.key),
3156
- confidence: 1
3157
- }
3158
- ] : keyTimelineFromNotes(sounding, { meters, totalBeats, budget });
3159
- const prevailingKey = _nullishCoalesce(prevailingKeyOf(keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
3388
+ const keys = opts.key !== void 0 ? statedKeyRegions(_chunkYDGMWH43cjs.resolveKey.call(void 0, opts.key), musicStart, totalBeats) : keyTimelineFromNotes(sounding, { meters, totalBeats, budget });
3389
+ const prevailingKey = _nullishCoalesce(prevailingKeyOf(keys), () => ( _chunkYDGMWH43cjs.resolveKey.call(void 0, _chunkYDGMWH43cjs.majorKey.call(void 0, 0))));
3160
3390
  const keyAt = keyLookup(keys, prevailingKey);
3161
- const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
3391
+ const scaleAt = (beat) => _chunkYDGMWH43cjs.scaleOf.call(void 0, keyAt(beat));
3162
3392
  const segments = [];
3163
3393
  const segmentConfidence = [];
3164
3394
  const slotNotes = bucketNotesBySlot(sounding, grid, slotCount, {
@@ -3174,15 +3404,15 @@ function analyzeTimeline(notes, opts = {}, previous, dirty) {
3174
3404
  harmonicRhythm,
3175
3405
  opts.key !== void 0
3176
3406
  ) ? previous : void 0;
3177
- const dirtyFrom = carried === void 0 ? 0 : Math.max(0, _nullishCoalesce(_optionalChain([dirty, 'optionalAccess', _50 => _50.from]), () => ( 0)));
3178
- const dirtyTo = carried === void 0 ? slotCount : Math.min(slotCount, _nullishCoalesce(_optionalChain([dirty, 'optionalAccess', _51 => _51.to]), () => ( slotCount)));
3179
- const tables = _nullishCoalesce(_optionalChain([carried, 'optionalAccess', _52 => _52.tables]), () => ( createBoundaryTables(dynamic ? slotCount : 0, budget)));
3407
+ const dirtyFrom = carried === void 0 ? 0 : Math.max(0, _nullishCoalesce(_optionalChain([dirty, 'optionalAccess', _53 => _53.from]), () => ( 0)));
3408
+ const dirtyTo = carried === void 0 ? slotCount : Math.min(slotCount, _nullishCoalesce(_optionalChain([dirty, 'optionalAccess', _54 => _54.to]), () => ( slotCount)));
3409
+ const tables = _nullishCoalesce(_optionalChain([carried, 'optionalAccess', _55 => _55.tables]), () => ( createBoundaryTables(dynamic ? slotCount : 0, budget)));
3180
3410
  if (carried !== void 0) {
3181
3411
  carried.tables = void 0;
3182
3412
  }
3183
3413
  const slotWeights = new Array(slotCount);
3184
3414
  for (let i = 0; i < slotCount; i += 1) {
3185
- const reusable = i < dirtyFrom || i >= dirtyTo ? _optionalChain([carried, 'optionalAccess', _53 => _53.slotWeights, 'access', _54 => _54[i]]) : void 0;
3415
+ const reusable = i < dirtyFrom || i >= dirtyTo ? _optionalChain([carried, 'optionalAccess', _56 => _56.slotWeights, 'access', _57 => _57[i]]) : void 0;
3186
3416
  if (reusable !== void 0) {
3187
3417
  slotWeights[i] = reusable;
3188
3418
  continue;
@@ -3211,14 +3441,29 @@ function analyzeTimeline(notes, opts = {}, previous, dirty) {
3211
3441
  const spanKeyWhole = keyAt(start);
3212
3442
  const id = windowId(start, end, spanKey);
3213
3443
  const cached = carried !== void 0 && spanIsClean(grid, span, dirtyFrom, dirtyTo) ? carried.windows.get(id) : void 0;
3214
- const inferred = cached !== void 0 ? cached : analyzeWindow(notesOfSpan(slotNotes, span, grid), start, end, meters, spanKeyWhole);
3444
+ const inferred = cached !== void 0 ? cached : analyzeWindow(
3445
+ notesOfSpan(slotNotes, span, grid),
3446
+ start,
3447
+ end,
3448
+ meters,
3449
+ spanKeyWhole,
3450
+ harmonicRhythm
3451
+ );
3215
3452
  windows.set(id, inferred);
3216
3453
  readings.push(inferred);
3217
3454
  readingKeys.push(spanKey);
3218
3455
  readingBounds.push({ start, end });
3219
3456
  }
3457
+ for (let i = readings.length - 1; i >= 0; i -= 1) {
3458
+ const spanKey = readingKeys[i];
3459
+ const root = spanKey === void 0 ? null : settledDiminishedRoot(readings, i, spanKey);
3460
+ const rooting = _optionalChain([readings, 'access', _58 => _58[i], 'optionalAccess', _59 => _59.rootings, 'optionalAccess', _60 => _60.find, 'call', _61 => _61((option) => option.chord.rootPc === root)]);
3461
+ if (rooting !== void 0 && root !== _optionalChain([readings, 'access', _62 => _62[i], 'optionalAccess', _63 => _63.chord, 'access', _64 => _64.rootPc])) {
3462
+ readings[i] = rooting;
3463
+ }
3464
+ }
3220
3465
  for (let i = 0; i < readings.length; i += 1) {
3221
- const offered = _optionalChain([readings, 'access', _55 => _55[i], 'optionalAccess', _56 => _56.augmented]);
3466
+ const offered = _optionalChain([readings, 'access', _65 => _65[i], 'optionalAccess', _66 => _66.augmented]);
3222
3467
  const spanKey = readingKeys[i];
3223
3468
  if (offered !== void 0 && spanKey !== void 0 && resolvesOntoDominant(readings, i, spanKey)) {
3224
3469
  readings[i] = offered;
@@ -3232,7 +3477,7 @@ function analyzeTimeline(notes, opts = {}, previous, dirty) {
3232
3477
  }
3233
3478
  const { start, end } = bounds;
3234
3479
  const last = segments[segments.length - 1];
3235
- if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) < _chunkS5BKZZGMcjs.BEAT_EPS) {
3480
+ if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) < _chunkS3QELJUEcjs.BEAT_EPS) {
3236
3481
  const lastLength = last.endBeat - last.startBeat;
3237
3482
  const length = end - start;
3238
3483
  const lastConfidence = _nullishCoalesce(segmentConfidence[segmentConfidence.length - 1], () => ( 0));
@@ -3273,11 +3518,11 @@ function detectCadences(timeline, key) {
3273
3518
  if (!prev || !cur) {
3274
3519
  continue;
3275
3520
  }
3276
- if (Math.abs(cur.startBeat - prev.endBeat) > _chunkS5BKZZGMcjs.BEAT_EPS) {
3521
+ if (Math.abs(cur.startBeat - prev.endBeat) > _chunkS3QELJUEcjs.BEAT_EPS) {
3277
3522
  continue;
3278
3523
  }
3279
3524
  const before = timeline.segments[i - 2];
3280
- const approach = before !== void 0 && Math.abs(prev.startBeat - before.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS ? before.chord : void 0;
3525
+ const approach = before !== void 0 && Math.abs(prev.startBeat - before.endBeat) <= _chunkS3QELJUEcjs.BEAT_EPS ? before.chord : void 0;
3281
3526
  const key2 = keyAt(cur.startBeat);
3282
3527
  const cadence = detectCadence(prev.chord, cur.chord, key2, {
3283
3528
  ...approach === void 0 ? {} : { approach }
@@ -3287,7 +3532,7 @@ function detectCadences(timeline, key) {
3287
3532
  }
3288
3533
  if (cadence.type === "half" && isCadentialSixFour(prev.chord, cur.chord, key2)) {
3289
3534
  const next = timeline.segments[i + 1];
3290
- if (next !== void 0 && Math.abs(next.startBeat - cur.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS) {
3535
+ if (next !== void 0 && Math.abs(next.startBeat - cur.endBeat) <= _chunkS3QELJUEcjs.BEAT_EPS) {
3291
3536
  const onward = detectCadence(cur.chord, next.chord, keyAt(next.startBeat)).type;
3292
3537
  if (onward === "authentic" || onward === "deceptive") {
3293
3538
  continue;
@@ -3305,15 +3550,15 @@ function phraseNotes(phrase) {
3305
3550
  if (Array.isArray(phrase)) {
3306
3551
  return phrase;
3307
3552
  }
3308
- return _chunkS5BKZZGMcjs.assertRecord.call(void 0, phrase, "melodic phrase").notes;
3553
+ return _chunkS3QELJUEcjs.assertRecord.call(void 0, phrase, "melodic phrase").notes;
3309
3554
  }
3310
3555
  function orderedNotes(notes, name, budget) {
3311
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, name, { allowNonPositiveDuration: true, budget });
3556
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, name, { allowNonPositiveDuration: true, budget });
3312
3557
  const sounding = notes.filter((note) => note.durationBeat > 0).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
3313
3558
  const line = [];
3314
3559
  for (const note of sounding) {
3315
3560
  const previous = line[line.length - 1];
3316
- if (previous !== void 0 && Math.abs(note.startBeat - previous.startBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS) {
3561
+ if (previous !== void 0 && Math.abs(note.startBeat - previous.startBeat) <= _chunkS3QELJUEcjs.BEAT_EPS) {
3317
3562
  line[line.length - 1] = note;
3318
3563
  continue;
3319
3564
  }
@@ -3324,14 +3569,14 @@ function orderedNotes(notes, name, budget) {
3324
3569
  function intervalsOf(notes) {
3325
3570
  const out = [];
3326
3571
  for (let i = 1; i < notes.length; i += 1) {
3327
- out.push((_nullishCoalesce(_optionalChain([notes, 'access', _57 => _57[i], 'optionalAccess', _58 => _58.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _59 => _59[i - 1], 'optionalAccess', _60 => _60.pitch]), () => ( 0))));
3572
+ out.push((_nullishCoalesce(_optionalChain([notes, 'access', _67 => _67[i], 'optionalAccess', _68 => _68.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _69 => _69[i - 1], 'optionalAccess', _70 => _70.pitch]), () => ( 0))));
3328
3573
  }
3329
3574
  return out;
3330
3575
  }
3331
3576
  function onsetGaps(notes) {
3332
3577
  const out = [];
3333
3578
  for (let i = 1; i < notes.length; i += 1) {
3334
- out.push((_nullishCoalesce(_optionalChain([notes, 'access', _61 => _61[i], 'optionalAccess', _62 => _62.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _63 => _63[i - 1], 'optionalAccess', _64 => _64.startBeat]), () => ( 0))));
3579
+ out.push((_nullishCoalesce(_optionalChain([notes, 'access', _71 => _71[i], 'optionalAccess', _72 => _72.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _73 => _73[i - 1], 'optionalAccess', _74 => _74.startBeat]), () => ( 0))));
3335
3580
  }
3336
3581
  return out;
3337
3582
  }
@@ -3341,14 +3586,14 @@ function retrogradeGaps(notes) {
3341
3586
  const later = notes[i];
3342
3587
  const earlier = notes[i - 1];
3343
3588
  out.push(
3344
- (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _65 => _65.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _66 => _66.durationBeat]), () => ( 0))) - ((_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _67 => _67.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _68 => _68.durationBeat]), () => ( 0))))
3589
+ (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _75 => _75.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _76 => _76.durationBeat]), () => ( 0))) - ((_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _77 => _77.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _78 => _78.durationBeat]), () => ( 0))))
3345
3590
  );
3346
3591
  }
3347
3592
  return out;
3348
3593
  }
3349
3594
  function rhythmProfile(gaps) {
3350
3595
  const unit = _nullishCoalesce(gaps[0], () => ( 0));
3351
- if (!(unit > _chunkS5BKZZGMcjs.BEAT_EPS)) {
3596
+ if (!(unit > _chunkS3QELJUEcjs.BEAT_EPS)) {
3352
3597
  return gaps.length === 0 ? [] : null;
3353
3598
  }
3354
3599
  return gaps.map((gap) => Math.round(gap / unit * RHYTHM_GRID) / RHYTHM_GRID);
@@ -3358,7 +3603,7 @@ function sameNumbers(a, b) {
3358
3603
  return false;
3359
3604
  }
3360
3605
  for (let i = 0; i < a.length; i += 1) {
3361
- if (Math.abs((_nullishCoalesce(a[i], () => ( 0))) - (_nullishCoalesce(b[i], () => ( 0)))) > _chunkS5BKZZGMcjs.BEAT_EPS) {
3606
+ if (Math.abs((_nullishCoalesce(a[i], () => ( 0))) - (_nullishCoalesce(b[i], () => ( 0)))) > _chunkS3QELJUEcjs.BEAT_EPS) {
3362
3607
  return false;
3363
3608
  }
3364
3609
  }
@@ -3369,17 +3614,13 @@ function roundTo(value, digits) {
3369
3614
  return Math.round(value * scale) / scale;
3370
3615
  }
3371
3616
  function endBeatOf(notes) {
3372
- let end = 0;
3373
- for (const note of notes) {
3374
- end = Math.max(end, note.startBeat + note.durationBeat);
3375
- }
3376
- return end;
3617
+ return latestSoundingEnd(notes);
3377
3618
  }
3378
3619
  function onsetSpan(notes) {
3379
3620
  if (notes.length < 2) {
3380
3621
  return 0;
3381
3622
  }
3382
- return (_nullishCoalesce(_optionalChain([notes, 'access', _69 => _69[notes.length - 1], 'optionalAccess', _70 => _70.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _71 => _71[0], 'optionalAccess', _72 => _72.startBeat]), () => ( 0)));
3623
+ return (_nullishCoalesce(_optionalChain([notes, 'access', _79 => _79[notes.length - 1], 'optionalAccess', _80 => _80.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _81 => _81[0], 'optionalAccess', _82 => _82.startBeat]), () => ( 0)));
3383
3624
  }
3384
3625
 
3385
3626
  // src/analyze/melody/contour.ts
@@ -3398,10 +3639,10 @@ function extremes(notes) {
3398
3639
  let peak = 0;
3399
3640
  let trough = 0;
3400
3641
  for (let i = 1; i < notes.length; i += 1) {
3401
- if ((_nullishCoalesce(_optionalChain([notes, 'access', _73 => _73[i], 'optionalAccess', _74 => _74.pitch]), () => ( 0))) > (_nullishCoalesce(_optionalChain([notes, 'access', _75 => _75[peak], 'optionalAccess', _76 => _76.pitch]), () => ( 0)))) {
3642
+ if ((_nullishCoalesce(_optionalChain([notes, 'access', _83 => _83[i], 'optionalAccess', _84 => _84.pitch]), () => ( 0))) > (_nullishCoalesce(_optionalChain([notes, 'access', _85 => _85[peak], 'optionalAccess', _86 => _86.pitch]), () => ( 0)))) {
3402
3643
  peak = i;
3403
3644
  }
3404
- if ((_nullishCoalesce(_optionalChain([notes, 'access', _77 => _77[i], 'optionalAccess', _78 => _78.pitch]), () => ( 0))) < (_nullishCoalesce(_optionalChain([notes, 'access', _79 => _79[trough], 'optionalAccess', _80 => _80.pitch]), () => ( 0)))) {
3645
+ if ((_nullishCoalesce(_optionalChain([notes, 'access', _87 => _87[i], 'optionalAccess', _88 => _88.pitch]), () => ( 0))) < (_nullishCoalesce(_optionalChain([notes, 'access', _89 => _89[trough], 'optionalAccess', _90 => _90.pitch]), () => ( 0)))) {
3405
3646
  trough = i;
3406
3647
  }
3407
3648
  }
@@ -3414,7 +3655,7 @@ function melodicContour(notes) {
3414
3655
  (step) => step > 0 ? "up" : step < 0 ? "down" : "same"
3415
3656
  );
3416
3657
  const { peak, trough } = extremes(line);
3417
- const range = (_nullishCoalesce(_optionalChain([line, 'access', _81 => _81[peak], 'optionalAccess', _82 => _82.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([line, 'access', _83 => _83[trough], 'optionalAccess', _84 => _84.pitch]), () => ( 0)));
3658
+ const range = (_nullishCoalesce(_optionalChain([line, 'access', _91 => _91[peak], 'optionalAccess', _92 => _92.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([line, 'access', _93 => _93[trough], 'optionalAccess', _94 => _94.pitch]), () => ( 0)));
3418
3659
  const motion = intervals.reduce((sum, step) => sum + Math.abs(step), 0);
3419
3660
  if (motion === 0) {
3420
3661
  return {
@@ -3426,8 +3667,8 @@ function melodicContour(notes) {
3426
3667
  rationale: line.length < 2 ? "Too short to have a direction, so the line is static" : "Every note at the same pitch, so the line is static"
3427
3668
  };
3428
3669
  }
3429
- const first = _nullishCoalesce(_optionalChain([line, 'access', _85 => _85[0], 'optionalAccess', _86 => _86.pitch]), () => ( 0));
3430
- const last = _nullishCoalesce(_optionalChain([line, 'access', _87 => _87[line.length - 1], 'optionalAccess', _88 => _88.pitch]), () => ( 0));
3670
+ const first = _nullishCoalesce(_optionalChain([line, 'access', _95 => _95[0], 'optionalAccess', _96 => _96.pitch]), () => ( 0));
3671
+ const last = _nullishCoalesce(_optionalChain([line, 'access', _97 => _97[line.length - 1], 'optionalAccess', _98 => _98.pitch]), () => ( 0));
3431
3672
  const net = last - first;
3432
3673
  const directness = Math.abs(net) / motion;
3433
3674
  const share = Math.round(directness * 100);
@@ -3439,10 +3680,10 @@ function melodicContour(notes) {
3439
3680
  peakIndex: peak,
3440
3681
  troughIndex: trough,
3441
3682
  range,
3442
- rationale: `${share}% of the motion runs ${net > 0 ? "up" : "down"}, so the line is ${shape}`
3683
+ rationale: `The net ${net > 0 ? "rise" : "fall"} is ${share}% of the motion, so the line is ${shape}`
3443
3684
  };
3444
3685
  }
3445
- const peakPitch = _nullishCoalesce(_optionalChain([line, 'access', _89 => _89[peak], 'optionalAccess', _90 => _90.pitch]), () => ( 0));
3686
+ const peakPitch = _nullishCoalesce(_optionalChain([line, 'access', _99 => _99[peak], 'optionalAccess', _100 => _100.pitch]), () => ( 0));
3446
3687
  const archMotion = peakPitch - first + (peakPitch - last);
3447
3688
  if (peak > 0 && peak < line.length - 1 && archMotion >= DIRECTIONAL_SHARE * motion) {
3448
3689
  return {
@@ -3460,7 +3701,7 @@ function melodicContour(notes) {
3460
3701
  peakIndex: peak,
3461
3702
  troughIndex: trough,
3462
3703
  range,
3463
- rationale: `Changes direction without settling either way (${share}% of the motion nets out), so the line is a wave`
3704
+ rationale: `Changes direction without settling either way (the net movement is ${share}% of the motion), so the line is a wave`
3464
3705
  };
3465
3706
  }
3466
3707
 
@@ -3506,22 +3747,22 @@ function listBeats(occurrences) {
3506
3747
  function motifFromGroup(group, notes) {
3507
3748
  const first = _nullishCoalesce(group.starts[0], () => ( 0));
3508
3749
  const prime = notes.slice(first, first + group.length).map((note) => ({ ...note }));
3509
- const primePitch = _nullishCoalesce(_optionalChain([prime, 'access', _91 => _91[0], 'optionalAccess', _92 => _92.pitch]), () => ( 0));
3750
+ const primePitch = _nullishCoalesce(_optionalChain([prime, 'access', _101 => _101[0], 'optionalAccess', _102 => _102.pitch]), () => ( 0));
3510
3751
  const primeSpan = onsetSpan(prime);
3511
3752
  const occurrences = group.starts.map((start) => {
3512
3753
  const statement = notes.slice(start, start + group.length);
3513
3754
  const span = onsetSpan(statement);
3514
3755
  return {
3515
3756
  noteIndex: start,
3516
- startBeat: _nullishCoalesce(_optionalChain([statement, 'access', _93 => _93[0], 'optionalAccess', _94 => _94.startBeat]), () => ( 0)),
3757
+ startBeat: _nullishCoalesce(_optionalChain([statement, 'access', _103 => _103[0], 'optionalAccess', _104 => _104.startBeat]), () => ( 0)),
3517
3758
  endBeat: endBeatOf(statement),
3518
- transpose: (_nullishCoalesce(_optionalChain([statement, 'access', _95 => _95[0], 'optionalAccess', _96 => _96.pitch]), () => ( 0))) - primePitch,
3519
- timeRatio: primeSpan > _chunkS5BKZZGMcjs.BEAT_EPS ? span / primeSpan : 1
3759
+ transpose: (_nullishCoalesce(_optionalChain([statement, 'access', _105 => _105[0], 'optionalAccess', _106 => _106.pitch]), () => ( 0))) - primePitch,
3760
+ timeRatio: primeSpan > _chunkS3QELJUEcjs.BEAT_EPS ? span / primeSpan : 1
3520
3761
  };
3521
3762
  });
3522
3763
  const shape = melodicContour(prime).shape;
3523
3764
  const transposed = occurrences.some((occurrence) => occurrence.transpose !== 0);
3524
- const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS);
3765
+ const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > _chunkS3QELJUEcjs.BEAT_EPS);
3525
3766
  const detail = [
3526
3767
  transposed ? "at more than one pitch level" : "at the same pitch level",
3527
3768
  stretched ? "and in more than one set of note values" : ""
@@ -3535,15 +3776,15 @@ function motifFromGroup(group, notes) {
3535
3776
  };
3536
3777
  }
3537
3778
  function extractMotifs(notes, opts = {}) {
3538
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
3539
- const minNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.minNotes, () => ( MIN_CELL_NOTES)), "motif minNotes", 2, 64);
3540
- const maxNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.maxNotes, () => ( MAX_CELL_NOTES)), "motif maxNotes", 2, 64);
3779
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
3780
+ const minNotes = _chunkS3QELJUEcjs.assertInteger.call(void 0, _nullishCoalesce(asked.minNotes, () => ( MIN_CELL_NOTES)), "motif minNotes", 2, 64);
3781
+ const maxNotes = _chunkS3QELJUEcjs.assertInteger.call(void 0, _nullishCoalesce(asked.maxNotes, () => ( MAX_CELL_NOTES)), "motif maxNotes", 2, 64);
3541
3782
  if (maxNotes < minNotes) {
3542
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3783
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
3543
3784
  `motif maxNotes must be at least minNotes ${minNotes}; received ${maxNotes}`
3544
3785
  );
3545
3786
  }
3546
- const minOccurrences = _chunkS5BKZZGMcjs.assertInteger.call(void 0,
3787
+ const minOccurrences = _chunkS3QELJUEcjs.assertInteger.call(void 0,
3547
3788
  _nullishCoalesce(asked.minOccurrences, () => ( MIN_OCCURRENCES)),
3548
3789
  "motif minOccurrences",
3549
3790
  2
@@ -3552,7 +3793,7 @@ function extractMotifs(notes, opts = {}) {
3552
3793
  if (sounding.length < minNotes) {
3553
3794
  return [];
3554
3795
  }
3555
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
3796
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
3556
3797
  sounding.length * (maxNotes - minNotes + 1),
3557
3798
  "motif windows",
3558
3799
  asked.budget
@@ -3599,7 +3840,7 @@ function extractMotifs(notes, opts = {}) {
3599
3840
  }
3600
3841
  }
3601
3842
  }
3602
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "motif subsumption comparisons", asked.budget);
3843
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, comparisons, "motif subsumption comparisons", asked.budget);
3603
3844
  if (!covered) {
3604
3845
  kept.push(candidate);
3605
3846
  let byStart = keptByLength.get(candidate.length);
@@ -3637,18 +3878,18 @@ function spellingOf(key) {
3637
3878
  if (key === void 0) {
3638
3879
  return "sharp";
3639
3880
  }
3640
- return _chunk3R3QVKY5cjs.keySignatureFifths.call(void 0, key.tonic, key.scale) < 0 ? "flat" : "sharp";
3881
+ return _chunkYDGMWH43cjs.keySignatureFifths.call(void 0, key.tonic, key.scale) < 0 ? "flat" : "sharp";
3641
3882
  }
3642
3883
  function intervalBetween(from, to, key) {
3643
- const spelling = spellingOf(key);
3644
- return _chunkS5BKZZGMcjs.spelledInterval.call(void 0, _chunkS5BKZZGMcjs.midiToNote.call(void 0, from, spelling), _chunkS5BKZZGMcjs.midiToNote.call(void 0, to, spelling));
3884
+ const start = _chunkS3QELJUEcjs.midiToNote.call(void 0, from, spellingOf(key));
3885
+ return _chunkS3QELJUEcjs.spelledInterval.call(void 0, start, _chunkS3QELJUEcjs.transposeNote.call(void 0, start, to - from));
3645
3886
  }
3646
3887
  function intervalName(interval) {
3647
3888
  return `${interval.descending && interval.semitones !== 0 ? "-" : ""}${interval.quality}${interval.number}`;
3648
3889
  }
3649
3890
  function ladderIndices(notes, key) {
3650
- const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
3651
- const offsets = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pc - root));
3891
+ const root = _chunkS3QELJUEcjs.pitchClassOf.call(void 0, key.rootPc);
3892
+ const offsets = _chunkYDGMWH43cjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => _chunkS3QELJUEcjs.pitchClassOf.call(void 0, pc - root));
3652
3893
  if (offsets.length === 0) {
3653
3894
  return null;
3654
3895
  }
@@ -3699,15 +3940,15 @@ function totalGap(gaps) {
3699
3940
  }
3700
3941
  function stretchRatio(modelGaps, answerGaps) {
3701
3942
  const span = totalGap(modelGaps);
3702
- return span > _chunkS5BKZZGMcjs.BEAT_EPS ? totalGap(answerGaps) / span : 1;
3943
+ return span > _chunkS3QELJUEcjs.BEAT_EPS ? totalGap(answerGaps) / span : 1;
3703
3944
  }
3704
3945
  function stretchPhrase(ratio2) {
3705
3946
  return `note values ${roundTo(ratio2, 3)}x the model's`;
3706
3947
  }
3707
3948
  function relateMotifs(a, b, keyLike) {
3708
- const key = keyLike === void 0 ? void 0 : _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
3709
- const model = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, a, "a").notes, "a.notes");
3710
- const answer = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, b, "b").notes, "b.notes");
3949
+ const key = keyLike === void 0 ? void 0 : _chunkYDGMWH43cjs.resolveKey.call(void 0, keyLike);
3950
+ const model = _chunkS3QELJUEcjs.assertArray.call(void 0, _chunkS3QELJUEcjs.assertRecord.call(void 0, a, "a").notes, "a.notes");
3951
+ const answer = _chunkS3QELJUEcjs.assertArray.call(void 0, _chunkS3QELJUEcjs.assertRecord.call(void 0, b, "b").notes, "b.notes");
3711
3952
  if (model.length === 0 || model.length !== answer.length) {
3712
3953
  return null;
3713
3954
  }
@@ -3722,12 +3963,12 @@ function relateMotifs(a, b, keyLike) {
3722
3963
  }
3723
3964
  const forward = sameNumbers(modelProfile, answerProfile);
3724
3965
  const backward = retroProfile !== null && sameNumbers(retroProfile, answerProfile);
3725
- const semitones = (_nullishCoalesce(_optionalChain([answer, 'access', _97 => _97[0], 'optionalAccess', _98 => _98.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([model, 'access', _99 => _99[0], 'optionalAccess', _100 => _100.pitch]), () => ( 0)));
3726
- const interval = intervalBetween(_nullishCoalesce(_optionalChain([model, 'access', _101 => _101[0], 'optionalAccess', _102 => _102.pitch]), () => ( 0)), _nullishCoalesce(_optionalChain([answer, 'access', _103 => _103[0], 'optionalAccess', _104 => _104.pitch]), () => ( 0)), key);
3727
- const gap = (_nullishCoalesce(_optionalChain([answer, 'access', _105 => _105[0], 'optionalAccess', _106 => _106.startBeat]), () => ( 0))) - endBeatOf(model);
3966
+ const semitones = (_nullishCoalesce(_optionalChain([answer, 'access', _107 => _107[0], 'optionalAccess', _108 => _108.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([model, 'access', _109 => _109[0], 'optionalAccess', _110 => _110.pitch]), () => ( 0)));
3967
+ const interval = intervalBetween(_nullishCoalesce(_optionalChain([model, 'access', _111 => _111[0], 'optionalAccess', _112 => _112.pitch]), () => ( 0)), _nullishCoalesce(_optionalChain([answer, 'access', _113 => _113[0], 'optionalAccess', _114 => _114.pitch]), () => ( 0)), key);
3968
+ const gap = (_nullishCoalesce(_optionalChain([answer, 'access', _115 => _115[0], 'optionalAccess', _116 => _116.startBeat]), () => ( 0))) - endBeatOf(model);
3728
3969
  const sequence = gap >= -HUMANIZE_ADJACENCY && gap <= HUMANIZE_ADJACENCY;
3729
3970
  const timeRatio = stretchRatio(modelGaps, answerGaps);
3730
- const stretched = Math.abs(timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS;
3971
+ const stretched = Math.abs(timeRatio - 1) > _chunkS3QELJUEcjs.BEAT_EPS;
3731
3972
  const shapes = pitchShapes(intervalsOf(model), intervalsOf(answer));
3732
3973
  const base = { sequence, semitones, interval, timeRatio };
3733
3974
  const backwardBase = { ...base, timeRatio: stretchRatio(retroGaps, answerGaps) };
@@ -3771,11 +4012,12 @@ function relateMotifs(a, b, keyLike) {
3771
4012
  }
3772
4013
  }
3773
4014
  }
3774
- if (forward && shapes.includes("inversion")) {
4015
+ const literalRetrograde = backward && shapes.includes("retrograde") && _optionalChain([answer, 'access', _117 => _117[0], 'optionalAccess', _118 => _118.pitch]) === _optionalChain([model, 'access', _119 => _119[model.length - 1], 'optionalAccess', _120 => _120.pitch]);
4016
+ if (forward && shapes.includes("inversion") && !(literalRetrograde && semitones !== 0)) {
3775
4017
  return {
3776
4018
  ...base,
3777
4019
  kind: "inversion",
3778
- rationale: "Inversion: every interval turned upside down about the first note" + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
4020
+ rationale: `Inversion: every interval turned upside down${semitones === 0 ? " about the first note" : ""}` + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
3779
4021
  };
3780
4022
  }
3781
4023
  if (backward && shapes.includes("retrograde")) {
@@ -3829,8 +4071,8 @@ function intervalCost(x, y) {
3829
4071
  return Math.min(1, Math.abs(x - y) / INTERVAL_TOLERANCE);
3830
4072
  }
3831
4073
  function gapCost(x, y) {
3832
- if (!(x > _chunkS5BKZZGMcjs.BEAT_EPS) || !(y > _chunkS5BKZZGMcjs.BEAT_EPS)) {
3833
- return Math.abs(x - y) > _chunkS5BKZZGMcjs.BEAT_EPS ? 1 : 0;
4074
+ if (!(x > _chunkS3QELJUEcjs.BEAT_EPS) || !(y > _chunkS3QELJUEcjs.BEAT_EPS)) {
4075
+ return Math.abs(x - y) > _chunkS3QELJUEcjs.BEAT_EPS ? 1 : 0;
3834
4076
  }
3835
4077
  return Math.min(1, Math.abs(Math.log2(x / y)));
3836
4078
  }
@@ -3839,6 +4081,7 @@ function compareMelodies(a, b) {
3839
4081
  const right = orderedNotes(phraseNotes(b), "melody b");
3840
4082
  const leftIntervals = intervalsOf(left);
3841
4083
  const rightIntervals = intervalsOf(right);
4084
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, leftIntervals.length * rightIntervals.length, "melody alignment");
3842
4085
  const pitchSimilarity = distanceToSimilarity(
3843
4086
  gradedEditDistance(leftIntervals, rightIntervals, intervalCost),
3844
4087
  leftIntervals.length,
@@ -3884,28 +4127,28 @@ function betterParent(candidate, current) {
3884
4127
  return candidate.index < current.index;
3885
4128
  }
3886
4129
  function motifGraph(line, motifs, opts = {}) {
3887
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
4130
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
3888
4131
  const variationThreshold = _nullishCoalesce(asked.variationThreshold, () => ( DEFAULT_VARIATION_THRESHOLD));
3889
- _chunkS5BKZZGMcjs.assertRange.call(void 0, variationThreshold, 0, 1, "motif graph variationThreshold");
4132
+ _chunkS3QELJUEcjs.assertRange.call(void 0, variationThreshold, 0, 1, "motif graph variationThreshold");
3890
4133
  const ordered = orderedNotes(line, "line", asked.budget);
3891
- const motifList = _chunkS5BKZZGMcjs.assertArray.call(void 0, motifs, "motifs");
4134
+ const motifList = _chunkS3QELJUEcjs.assertArray.call(void 0, motifs, "motifs");
3892
4135
  const infos = [];
3893
4136
  for (let mi = 0; mi < motifList.length; mi += 1) {
3894
- const motif = _chunkS5BKZZGMcjs.assertRecord.call(void 0, motifList[mi], `motifs[${mi}]`);
3895
- const notes = _chunkS5BKZZGMcjs.assertArray.call(void 0, motif.notes, `motifs[${mi}].notes`);
3896
- const occurrences = _chunkS5BKZZGMcjs.assertArray.call(void 0,
4137
+ const motif = _chunkS3QELJUEcjs.assertRecord.call(void 0, motifList[mi], `motifs[${mi}]`);
4138
+ const notes = _chunkS3QELJUEcjs.assertArray.call(void 0, motif.notes, `motifs[${mi}].notes`);
4139
+ const occurrences = _chunkS3QELJUEcjs.assertArray.call(void 0,
3897
4140
  motif.occurrences,
3898
4141
  `motifs[${mi}].occurrences`
3899
4142
  );
3900
4143
  for (let oi = 0; oi < occurrences.length; oi += 1) {
3901
- const occurrence = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
4144
+ const occurrence = _chunkS3QELJUEcjs.assertRecord.call(void 0,
3902
4145
  occurrences[oi],
3903
4146
  `motifs[${mi}].occurrences[${oi}]`
3904
4147
  );
3905
4148
  const { noteIndex } = occurrence;
3906
- const onLine = Number.isInteger(noteIndex) && noteIndex >= 0 && noteIndex + notes.length <= ordered.length && Math.abs((_nullishCoalesce(_optionalChain([ordered, 'access', _107 => _107[noteIndex], 'optionalAccess', _108 => _108.startBeat]), () => ( Number.NaN))) - occurrence.startBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS;
4149
+ const onLine = Number.isInteger(noteIndex) && noteIndex >= 0 && noteIndex + notes.length <= ordered.length && Math.abs((_nullishCoalesce(_optionalChain([ordered, 'access', _121 => _121[noteIndex], 'optionalAccess', _122 => _122.startBeat]), () => ( Number.NaN))) - occurrence.startBeat) <= _chunkS3QELJUEcjs.BEAT_EPS;
3907
4150
  if (!onLine) {
3908
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4151
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
3909
4152
  `motif occurrence motifs[${mi}].occurrences[${oi}] does not lie on the line`
3910
4153
  );
3911
4154
  }
@@ -3922,7 +4165,7 @@ function motifGraph(line, motifs, opts = {}) {
3922
4165
  }
3923
4166
  infos.sort((a, b) => a.node.startBeat - b.node.startBeat || a.node.motif - b.node.motif);
3924
4167
  const nodeCount = infos.length;
3925
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, nodeCount * nodeCount, "motif graph comparisons", asked.budget);
4168
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, nodeCount * nodeCount, "motif graph comparisons", asked.budget);
3926
4169
  const edges = [];
3927
4170
  for (let to = 1; to < nodeCount; to += 1) {
3928
4171
  const toInfo = infos[to];
@@ -3953,16 +4196,22 @@ function motifGraph(line, motifs, opts = {}) {
3953
4196
 
3954
4197
  // src/analyze/reduction/index.ts
3955
4198
  var REDUCTION_LEVELS = Object.freeze(["structural", "passing", "neighbor"]);
3956
- var TONIC_ARRIVING = /* @__PURE__ */ new Set(["authentic", "plagal", "modal", "deceptive"]);
4199
+ var TONIC_ARRIVING_CADENCES = [
4200
+ "authentic",
4201
+ "plagal",
4202
+ "modal",
4203
+ "deceptive"
4204
+ ];
4205
+ var TONIC_ARRIVING = new Set(TONIC_ARRIVING_CADENCES);
3957
4206
  function signedStep(fromPc, toPc) {
3958
- return (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, toPc) - _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, fromPc) + 18) % 12 - 6;
4207
+ return (_chunkS3QELJUEcjs.pitchClassOf.call(void 0, toPc) - _chunkS3QELJUEcjs.pitchClassOf.call(void 0, fromPc) + 18) % 12 - 6;
3959
4208
  }
3960
4209
  function isStep2(distance) {
3961
4210
  const size = Math.abs(distance);
3962
4211
  return size === 1 || size === 2;
3963
4212
  }
3964
4213
  function touches(before, after) {
3965
- return Math.abs(after.startBeat - before.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS;
4214
+ return Math.abs(after.startBeat - before.endBeat) <= _chunkS3QELJUEcjs.BEAT_EPS;
3966
4215
  }
3967
4216
  function salienceFloors(durations) {
3968
4217
  const order = durations.map((_, index) => index).sort((a, b) => {
@@ -3989,7 +4238,7 @@ function salienceFloors(durations) {
3989
4238
  return floors;
3990
4239
  }
3991
4240
  function isTonicChord(chord, key) {
3992
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 0 && !isAppliedDominant(chord, key);
4241
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 0 && !isAppliedDominant(chord, key);
3993
4242
  }
3994
4243
  function functionalReason(segment, before, after, keyAt) {
3995
4244
  const key = keyAt(segment.startBeat);
@@ -3999,7 +4248,7 @@ function functionalReason(segment, before, after, keyAt) {
3999
4248
  if (isTonicChord(segment.chord, key)) {
4000
4249
  return { kind: "tonic" };
4001
4250
  }
4002
- if (_chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, segment.chord, key)) {
4251
+ if (_chunkYDGMWH43cjs.isDominantChordOf.call(void 0, segment.chord, key)) {
4003
4252
  return { kind: "dominant" };
4004
4253
  }
4005
4254
  if (before !== void 0) {
@@ -4028,7 +4277,7 @@ function figureReason(segment, before, after) {
4028
4277
  if (incoming > 0 === outgoing > 0) {
4029
4278
  return { kind: "passing" };
4030
4279
  }
4031
- return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, before.chord.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, after.chord.rootPc) ? { kind: "neighbor" } : null;
4280
+ return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, before.chord.rootPc) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, after.chord.rootPc) ? { kind: "neighbor" } : null;
4032
4281
  }
4033
4282
  function describe2(reason) {
4034
4283
  switch (reason.kind) {
@@ -4069,9 +4318,9 @@ function levelOf(reason) {
4069
4318
  }
4070
4319
  function reduceProgression(timeline, key, opts = {}) {
4071
4320
  assertChordTimeline(timeline);
4072
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
4321
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
4073
4322
  const keyAt = keyScaleAt(key);
4074
- const basis = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.basis, () => ( "function")), REDUCTION_BASES, "reduction basis");
4323
+ const basis = _chunkS3QELJUEcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.basis, () => ( "function")), REDUCTION_BASES, "reduction basis");
4075
4324
  const reading = BASIS_READINGS[basis];
4076
4325
  const segments = timeline.segments;
4077
4326
  const floors = basis === "duration" ? salienceFloors(segments.map((segment) => segment.endBeat - segment.startBeat)) : [];
@@ -4104,27 +4353,38 @@ function reduceProgression(timeline, key, opts = {}) {
4104
4353
  return result;
4105
4354
  }
4106
4355
 
4356
+ // src/analyze/reference/types.ts
4357
+ var REFERENCE_PROFILE_VERSION = 1;
4358
+ var PHRASE_OUTLINE_POINTS = 8;
4359
+ var MIDI_MAX = 127;
4360
+
4107
4361
  // src/analyze/rhythm/internal.ts
4108
4362
  function isMultipleOf(value, unit) {
4109
4363
  if (unit <= 0) {
4110
4364
  return false;
4111
4365
  }
4112
4366
  const ratio2 = value / unit;
4113
- return Math.abs(ratio2 - Math.round(ratio2)) < _chunkS5BKZZGMcjs.BEAT_EPS;
4367
+ return Math.abs(ratio2 - Math.round(ratio2)) < _chunkS3QELJUEcjs.BEAT_EPS;
4114
4368
  }
4115
4369
  function rhythmLevel(beatInQuarters, meters) {
4116
- const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, beatInQuarters, meters);
4117
- const offset = beatInQuarters - _chunkS5BKZZGMcjs.barStartBeat.call(void 0, beatInQuarters, meters);
4118
- const pulse = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
4370
+ const ts = _chunkS3QELJUEcjs.meterAt.call(void 0, beatInQuarters, meters);
4371
+ const offset = beatInQuarters - _chunkS3QELJUEcjs.barStartBeat.call(void 0, beatInQuarters, meters);
4372
+ const pulse = _chunkS3QELJUEcjs.pulseBeats.call(void 0, ts);
4119
4373
  if (isMultipleOf(offset, pulse)) {
4120
- return _chunkS5BKZZGMcjs.metricWeight.call(void 0, beatInQuarters, meters) + 2;
4374
+ return _chunkS3QELJUEcjs.metricWeight.call(void 0, beatInQuarters, meters) + 2;
4121
4375
  }
4122
- const subUnit = pulse / (_chunkS5BKZZGMcjs.isCompound.call(void 0, ts) ? 3 : 2);
4376
+ const subUnit = pulse / (_chunkS3QELJUEcjs.isCompound.call(void 0, ts) ? 3 : 2);
4123
4377
  return isMultipleOf(offset, subUnit) ? 1 : 0;
4124
4378
  }
4379
+ var IOI_MIN_EXPONENT = -4;
4380
+ var IOI_MAX_EXPONENT = 4;
4381
+ var IOI_EXPONENT_STEP = 0.5;
4125
4382
  function ioiBinIndex(ioi) {
4126
- const exponent = Math.min(4, Math.max(-4, Math.round(2 * Math.log2(ioi)) / 2));
4127
- return Math.round((exponent + 4) / 0.5);
4383
+ const exponent = Math.min(
4384
+ IOI_MAX_EXPONENT,
4385
+ Math.max(IOI_MIN_EXPONENT, Math.round(2 * Math.log2(ioi)) / 2)
4386
+ );
4387
+ return Math.round((exponent - IOI_MIN_EXPONENT) / IOI_EXPONENT_STEP);
4128
4388
  }
4129
4389
 
4130
4390
  // src/analyze/rhythm/index.ts
@@ -4134,6 +4394,8 @@ var RHYTHM_OFF_GRID_TOLERANCE = 1 / 32;
4134
4394
  var RHYTHM_IOI_BINS = Object.freeze(
4135
4395
  Array.from({ length: 17 }, (_, i) => 2 ** (-4 + i * 0.5))
4136
4396
  );
4397
+ var BUDGET_SLOTS_PER_BAR = 48;
4398
+ var RHYTHM_LEVEL_COUNT = 6;
4137
4399
  function emptyAnalysis() {
4138
4400
  return {
4139
4401
  startBeat: 0,
@@ -4142,7 +4404,7 @@ function emptyAnalysis() {
4142
4404
  onsets: 0,
4143
4405
  onsetDensity: 0,
4144
4406
  barOnsets: [],
4145
- onsetLevels: new Array(6).fill(0),
4407
+ onsetLevels: new Array(RHYTHM_LEVEL_COUNT).fill(0),
4146
4408
  barPositions: [],
4147
4409
  interOnsetShares: new Array(RHYTHM_IOI_BINS.length).fill(0),
4148
4410
  restRatio: 0,
@@ -4166,8 +4428,8 @@ function foldOnsets(sounding) {
4166
4428
  return onsets;
4167
4429
  }
4168
4430
  function snapOnset(onset, meters) {
4169
- const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, onset.beat, meters);
4170
- const barStart = _chunkS5BKZZGMcjs.barStartBeat.call(void 0, onset.beat, meters);
4431
+ const ts = _chunkS3QELJUEcjs.meterAt.call(void 0, onset.beat, meters);
4432
+ const barStart = _chunkS3QELJUEcjs.barStartBeat.call(void 0, onset.beat, meters);
4171
4433
  const offset = onset.beat - barStart;
4172
4434
  const slot = Math.round(offset / SLOT_WIDTH);
4173
4435
  const snapped = barStart + slot * SLOT_WIDTH;
@@ -4177,13 +4439,47 @@ function snapOnset(onset, meters) {
4177
4439
  function tsKey(ts) {
4178
4440
  return ts.grouping === void 0 ? `${ts.numerator}/${ts.denominator}` : `${ts.numerator}/${ts.denominator}:${ts.grouping.join("+")}`;
4179
4441
  }
4180
- function barGridBeats(bar, meters) {
4181
- const barStart = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
4182
- const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, barStart, meters);
4183
- const barLength = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, barStart, meters);
4184
- const subUnit = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts) / (_chunkS5BKZZGMcjs.isCompound.call(void 0, ts) ? 3 : 2);
4442
+ function pulseKey(bar, index) {
4443
+ return `${bar}/${index}`;
4444
+ }
4445
+ function otherDivisionPulses(snapped, meters) {
4446
+ const pulses = /* @__PURE__ */ new Set();
4447
+ for (const onset of snapped) {
4448
+ const pulse = _chunkS3QELJUEcjs.pulseBeats.call(void 0, onset.ts);
4449
+ const offset = onset.snapped - onset.barStart;
4450
+ const index = Math.floor(offset / pulse + _chunkS3QELJUEcjs.BEAT_EPS);
4451
+ const within = offset - index * pulse;
4452
+ const [used, other] = _chunkS3QELJUEcjs.isCompound.call(void 0, onset.ts) ? [3, 2] : [2, 3];
4453
+ if (isMultipleOf(within, pulse / other) && !isMultipleOf(within, pulse / used)) {
4454
+ pulses.add(pulseKey(_chunkS3QELJUEcjs.barIndexAt.call(void 0, onset.beat, meters), index));
4455
+ }
4456
+ }
4457
+ return pulses;
4458
+ }
4459
+ function barGridPoints(bar, meters, otherPulses) {
4460
+ const barStart = _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
4461
+ const ts = _chunkS3QELJUEcjs.meterAt.call(void 0, barStart, meters);
4462
+ const barLength = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, barStart, meters);
4463
+ const pulse = _chunkS3QELJUEcjs.pulseBeats.call(void 0, ts);
4464
+ const [used, other] = _chunkS3QELJUEcjs.isCompound.call(void 0, ts) ? [3, 2] : [2, 3];
4465
+ const subUnit = pulse / used;
4185
4466
  const steps = Math.max(1, Math.round(barLength / subUnit));
4186
- return Array.from({ length: steps }, (_, k) => barStart + k * subUnit);
4467
+ const points = [];
4468
+ for (let k = 0; k < steps; k += 1) {
4469
+ const index = Math.floor(k * subUnit / pulse + _chunkS3QELJUEcjs.BEAT_EPS);
4470
+ if (k % used === 0 || !otherPulses.has(pulseKey(bar, index))) {
4471
+ const beat = barStart + k * subUnit;
4472
+ points.push({ beat, level: rhythmLevel(beat, meters) });
4473
+ }
4474
+ }
4475
+ for (let index = 0; index * pulse < steps * subUnit - _chunkS3QELJUEcjs.BEAT_EPS; index += 1) {
4476
+ if (otherPulses.has(pulseKey(bar, index))) {
4477
+ for (let m = 1; m < other; m += 1) {
4478
+ points.push({ beat: barStart + index * pulse + m * pulse / other, level: 1 });
4479
+ }
4480
+ }
4481
+ }
4482
+ return points.sort((a, b) => a.beat - b.beat);
4187
4483
  }
4188
4484
  function restRatioOf(sounding, windowStart, windowEnd) {
4189
4485
  const span = windowEnd - windowStart;
@@ -4217,13 +4513,14 @@ function syncopationOf(snapped, startBeat, endBeat, meters) {
4217
4513
  if (snapped.length === 0) {
4218
4514
  return 0;
4219
4515
  }
4220
- const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meters);
4221
- const lastBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, endBeat, meters);
4516
+ const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, startBeat, meters);
4517
+ const lastBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, endBeat, meters);
4518
+ const otherPulses = otherDivisionPulses(snapped, meters);
4222
4519
  const points = [];
4223
4520
  for (let bar = firstBar; bar <= lastBar; bar += 1) {
4224
- for (const beat of barGridBeats(bar, meters)) {
4225
- if (beat > startBeat + _chunkS5BKZZGMcjs.BEAT_EPS && beat <= endBeat + _chunkS5BKZZGMcjs.BEAT_EPS) {
4226
- points.push({ beat, level: rhythmLevel(beat, meters) });
4521
+ for (const point of barGridPoints(bar, meters, otherPulses)) {
4522
+ if (point.beat > startBeat + _chunkS3QELJUEcjs.BEAT_EPS && point.beat <= endBeat + _chunkS3QELJUEcjs.BEAT_EPS) {
4523
+ points.push(point);
4227
4524
  }
4228
4525
  }
4229
4526
  }
@@ -4240,51 +4537,59 @@ function syncopationOf(snapped, startBeat, endBeat, meters) {
4240
4537
  }
4241
4538
  const next = snapped[i + 1];
4242
4539
  const windowEnd = next !== void 0 ? next.snapped : onset.end;
4243
- while (ptr < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _109 => _109[ptr], 'optionalAccess', _110 => _110.beat]), () => ( Number.POSITIVE_INFINITY))) <= onset.snapped + _chunkS5BKZZGMcjs.BEAT_EPS) {
4540
+ while (ptr < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _123 => _123[ptr], 'optionalAccess', _124 => _124.beat]), () => ( Number.POSITIVE_INFINITY))) <= onset.snapped + _chunkS3QELJUEcjs.BEAT_EPS) {
4244
4541
  ptr += 1;
4245
4542
  }
4543
+ const here = ptr > 0 ? points[ptr - 1] : void 0;
4544
+ const onsetLevel = here !== void 0 && Math.abs(here.beat - onset.snapped) < _chunkS3QELJUEcjs.BEAT_EPS ? Math.max(onset.level, here.level) : onset.level;
4246
4545
  let scan = ptr;
4247
4546
  let maxLevel = 0;
4248
- while (scan < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _111 => _111[scan], 'optionalAccess', _112 => _112.beat]), () => ( Number.POSITIVE_INFINITY))) < windowEnd - _chunkS5BKZZGMcjs.BEAT_EPS) {
4249
- maxLevel = Math.max(maxLevel, _nullishCoalesce(_optionalChain([points, 'access', _113 => _113[scan], 'optionalAccess', _114 => _114.level]), () => ( 0)));
4547
+ while (scan < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _125 => _125[scan], 'optionalAccess', _126 => _126.beat]), () => ( Number.POSITIVE_INFINITY))) < windowEnd - _chunkS3QELJUEcjs.BEAT_EPS) {
4548
+ maxLevel = Math.max(maxLevel, _nullishCoalesce(_optionalChain([points, 'access', _127 => _127[scan], 'optionalAccess', _128 => _128.level]), () => ( 0)));
4250
4549
  scan += 1;
4251
4550
  }
4252
- if (maxLevel > onset.level) {
4253
- numerator += maxLevel - onset.level;
4551
+ if (maxLevel > onsetLevel) {
4552
+ numerator += maxLevel - onsetLevel;
4254
4553
  }
4255
4554
  ptr = scan;
4256
4555
  }
4257
4556
  return numerator / normalizer;
4258
4557
  }
4259
4558
  function analyzeRhythm(notes, opts = {}) {
4260
- const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "rhythm meters");
4261
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "rhythm notes", { allowNonPositiveDuration: true, budget: opts.budget });
4559
+ return rhythmOver(notes, opts, void 0);
4560
+ }
4561
+ function rhythmOver(notes, opts, windowStart) {
4562
+ const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "rhythm meters");
4563
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "rhythm notes", { allowNonPositiveDuration: true, budget: opts.budget });
4262
4564
  const sounding = notes.filter((note) => note.durationBeat > 0);
4565
+ const startBeat = _nullishCoalesce(windowStart, () => ( barGridStart(firstSoundingBeat(sounding), meters)));
4566
+ const endBeat = resolveSpanEnd(
4567
+ opts.totalBeats,
4568
+ startBeat,
4569
+ latestSoundingEnd(sounding),
4570
+ "rhythm totalBeats"
4571
+ );
4263
4572
  if (sounding.length === 0) {
4264
4573
  return emptyAnalysis();
4265
4574
  }
4266
- const firstOnset = sounding.reduce(
4267
- (min, n) => Math.min(min, n.startBeat),
4268
- Number.POSITIVE_INFINITY
4269
- );
4270
- const startBeat = barGridStart(firstOnset, meters);
4271
- const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
4272
- const endBeat = Math.max(startBeat, _nullishCoalesce(opts.totalBeats, () => ( lastEnd)));
4273
- _chunkS5BKZZGMcjs.assertRange.call(void 0, endBeat, 0, Number.MAX_SAFE_INTEGER, "rhythm totalBeats");
4274
4575
  const bars = barSpanOf(startBeat, endBeat, meters);
4275
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, Math.ceil(bars) * 48, "rhythm analysis slots", opts.budget);
4576
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
4577
+ Math.ceil(bars) * BUDGET_SLOTS_PER_BAR,
4578
+ "rhythm analysis slots",
4579
+ opts.budget
4580
+ );
4276
4581
  const onsets = foldOnsets(sounding);
4277
4582
  const snapped = onsets.map((onset) => snapOnset(onset, meters));
4278
4583
  const onsetDensity = bars > 0 ? onsets.length / bars : 0;
4279
- const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meters);
4584
+ const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, startBeat, meters);
4280
4585
  const barOnsets = new Array(lastBarOf(meters, startBeat, endBeat) - firstBar + 1).fill(0);
4281
4586
  for (const onset of onsets) {
4282
- const index = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, onset.beat, meters) - firstBar;
4587
+ const index = _chunkS3QELJUEcjs.barIndexAt.call(void 0, onset.beat, meters) - firstBar;
4283
4588
  if (index >= 0 && index < barOnsets.length) {
4284
4589
  barOnsets[index] = (_nullishCoalesce(barOnsets[index], () => ( 0))) + 1;
4285
4590
  }
4286
4591
  }
4287
- const onsetLevels = new Array(6).fill(0);
4592
+ const onsetLevels = new Array(RHYTHM_LEVEL_COUNT).fill(0);
4288
4593
  let offGridCount = 0;
4289
4594
  for (const onset of snapped) {
4290
4595
  onsetLevels[onset.level] = (_nullishCoalesce(onsetLevels[onset.level], () => ( 0))) + 1;
@@ -4303,7 +4608,7 @@ function analyzeRhythm(notes, opts = {}) {
4303
4608
  if (index === void 0) {
4304
4609
  index = profiles.length;
4305
4610
  profileIndex.set(key, index);
4306
- const slotsPerBar = Math.ceil(_chunkS5BKZZGMcjs.beatsPerBar.call(void 0, onset.ts) * RHYTHM_SLOTS_PER_BEAT);
4611
+ const slotsPerBar = Math.ceil(_chunkS3QELJUEcjs.beatsPerBar.call(void 0, onset.ts) * RHYTHM_SLOTS_PER_BEAT);
4307
4612
  profiles.push({
4308
4613
  ts: onset.ts,
4309
4614
  slotsPerBar,
@@ -4357,40 +4662,37 @@ function analyzeRhythm(notes, opts = {}) {
4357
4662
  };
4358
4663
  }
4359
4664
 
4360
- // src/analyze/reference/types.ts
4361
- var REFERENCE_PROFILE_VERSION = 1;
4362
-
4363
4665
  // src/analyze/reference/validate.ts
4364
4666
  var MASS_TOLERANCE = 1e-9;
4365
4667
  function assertString(value, name) {
4366
4668
  if (typeof value !== "string") {
4367
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be a string; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, value)}`);
4669
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must be a string; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, value)}`);
4368
4670
  }
4369
4671
  return value;
4370
4672
  }
4371
4673
  function assertSpanOrder(startBeat, endBeat, name) {
4372
4674
  if (endBeat < startBeat) {
4373
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4675
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4374
4676
  `${name}.endBeat must be at least ${name}.startBeat; received ${endBeat} < ${startBeat}`
4375
4677
  );
4376
4678
  }
4377
4679
  }
4378
4680
  function assertNullable(value, name, validate) {
4379
4681
  if (value === void 0) {
4380
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be present; received undefined`);
4682
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must be present; received undefined`);
4381
4683
  }
4382
4684
  return value === null ? null : validate(value, name);
4383
4685
  }
4384
4686
  function assertMassDistribution(value, length, name, allowAllZero) {
4385
- const shares = _chunkS5BKZZGMcjs.assertArray.call(void 0, value, name);
4687
+ const shares = _chunkS3QELJUEcjs.assertArray.call(void 0, value, name);
4386
4688
  if (shares.length !== length) {
4387
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must have length ${length}; received ${shares.length}`);
4689
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must have length ${length}; received ${shares.length}`);
4388
4690
  }
4389
4691
  let sum = 0;
4390
4692
  for (let index = 0; index < shares.length; index += 1) {
4391
- const share = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, shares[index], `${name}[${index}]`);
4693
+ const share = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, shares[index], `${name}[${index}]`);
4392
4694
  if (share < 0) {
4393
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}[${index}] must be at least 0; received ${share}`);
4695
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}[${index}] must be at least 0; received ${share}`);
4394
4696
  }
4395
4697
  sum += share;
4396
4698
  }
@@ -4398,37 +4700,37 @@ function assertMassDistribution(value, length, name, allowAllZero) {
4398
4700
  return shares;
4399
4701
  }
4400
4702
  if (Math.abs(sum - 1) > MASS_TOLERANCE) {
4401
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must sum to 1; received ${sum}`);
4703
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must sum to 1; received ${sum}`);
4402
4704
  }
4403
4705
  return shares;
4404
4706
  }
4405
4707
  function assertBarPositionProfile(value, name) {
4406
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4407
- _chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, read.ts, `${name}.ts`);
4408
- const slotsPerBar = _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, read.slotsPerBar, `${name}.slotsPerBar`);
4409
- const onsets = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
4708
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4709
+ _chunkS3QELJUEcjs.assertTimeSignature.call(void 0, read.ts, `${name}.ts`);
4710
+ const slotsPerBar = _chunkS3QELJUEcjs.assertPositiveInt.call(void 0, read.slotsPerBar, `${name}.slotsPerBar`);
4711
+ const onsets = _chunkS3QELJUEcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
4410
4712
  assertMassDistribution(read.shares, slotsPerBar, `${name}.shares`, onsets === 0);
4411
4713
  return read;
4412
4714
  }
4413
4715
  function assertRhythmAnalysis(value, name) {
4414
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4415
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4416
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4716
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4717
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4718
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4417
4719
  assertSpanOrder(startBeat, endBeat, name);
4418
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4419
- const onsets = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
4420
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
4421
- const barOnsets = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.barOnsets, `${name}.barOnsets`);
4720
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4721
+ const onsets = _chunkS3QELJUEcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
4722
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
4723
+ const barOnsets = _chunkS3QELJUEcjs.assertArray.call(void 0, read.barOnsets, `${name}.barOnsets`);
4422
4724
  for (let index = 0; index < barOnsets.length; index += 1) {
4423
- _chunkS5BKZZGMcjs.assertRange.call(void 0,
4725
+ _chunkS3QELJUEcjs.assertRange.call(void 0,
4424
4726
  barOnsets[index],
4425
4727
  0,
4426
4728
  Number.MAX_SAFE_INTEGER,
4427
4729
  `${name}.barOnsets[${index}]`
4428
4730
  );
4429
4731
  }
4430
- assertMassDistribution(read.onsetLevels, 6, `${name}.onsetLevels`, onsets === 0);
4431
- const barPositions = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.barPositions, `${name}.barPositions`);
4732
+ assertMassDistribution(read.onsetLevels, RHYTHM_LEVEL_COUNT, `${name}.onsetLevels`, onsets === 0);
4733
+ const barPositions = _chunkS3QELJUEcjs.assertArray.call(void 0, read.barPositions, `${name}.barPositions`);
4432
4734
  for (let index = 0; index < barPositions.length; index += 1) {
4433
4735
  assertBarPositionProfile(barPositions[index], `${name}.barPositions[${index}]`);
4434
4736
  }
@@ -4438,67 +4740,71 @@ function assertRhythmAnalysis(value, name) {
4438
4740
  `${name}.interOnsetShares`,
4439
4741
  onsets === 0
4440
4742
  );
4441
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
4442
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.syncopation, 0, 1, `${name}.syncopation`);
4443
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.offGridRatio, 0, 1, `${name}.offGridRatio`);
4743
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
4744
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.syncopation, 0, 1, `${name}.syncopation`);
4745
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.offGridRatio, 0, 1, `${name}.offGridRatio`);
4444
4746
  assertString(read.rationale, `${name}.rationale`);
4445
4747
  return read;
4446
4748
  }
4447
4749
  function assertFormSection(value, name, sectionCount) {
4448
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4750
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4449
4751
  assertString(read.label, `${name}.label`);
4450
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4451
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4752
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4753
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4452
4754
  assertSpanOrder(startBeat, endBeat, name);
4453
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4454
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.firstOccurrence, `${name}.firstOccurrence`, 0, Math.max(sectionCount - 1, 0));
4455
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
4755
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4756
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, read.firstOccurrence, `${name}.firstOccurrence`, 0, Math.max(sectionCount - 1, 0));
4757
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
4456
4758
  assertString(read.rationale, `${name}.rationale`);
4457
4759
  return read;
4458
4760
  }
4459
4761
  function assertReferenceCadence(value, name) {
4460
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4461
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.atBeat, `${name}.atBeat`);
4462
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.type, CADENCE_TYPES, `${name}.type`);
4463
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.weight, 0, 1, `${name}.weight`);
4762
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4763
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.atBeat, `${name}.atBeat`);
4764
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, read.type, CADENCE_TYPES, `${name}.type`);
4765
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.weight, 0, 1, `${name}.weight`);
4464
4766
  return read;
4465
4767
  }
4466
4768
  function assertReferencePhraseMelody(value, name) {
4467
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4468
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.shape, MELODIC_CONTOUR_SHAPES, `${name}.shape`);
4469
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, read.low, `${name}.low`);
4470
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, read.high, `${name}.high`);
4471
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.mean, 0, 127, `${name}.mean`);
4472
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.peakPitch, 0, 127, `${name}.peakPitch`);
4473
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.peakPosition, 0, 1, `${name}.peakPosition`);
4474
- const outline = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.outline, `${name}.outline`);
4475
- if (outline.length !== 8) {
4476
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.outline must have length 8; received ${outline.length}`);
4769
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4770
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, read.shape, MELODIC_CONTOUR_SHAPES, `${name}.shape`);
4771
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, read.low, `${name}.low`);
4772
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, read.high, `${name}.high`);
4773
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.mean, 0, MIDI_MAX, `${name}.mean`);
4774
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.peakPitch, 0, MIDI_MAX, `${name}.peakPitch`);
4775
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.peakPosition, 0, 1, `${name}.peakPosition`);
4776
+ const outline = _chunkS3QELJUEcjs.assertArray.call(void 0, read.outline, `${name}.outline`);
4777
+ if (outline.length !== PHRASE_OUTLINE_POINTS) {
4778
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4779
+ `${name}.outline must have length ${PHRASE_OUTLINE_POINTS}; received ${outline.length}`
4780
+ );
4477
4781
  }
4478
4782
  for (let index = 0; index < outline.length; index += 1) {
4479
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, outline[index], `${name}.outline[${index}]`);
4783
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, outline[index], `${name}.outline[${index}]`);
4480
4784
  }
4481
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
4482
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
4785
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
4786
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
4483
4787
  return read;
4484
4788
  }
4485
- function assertReferencePhrase(value, name, sectionCount, nodeCount) {
4486
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4487
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4488
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4789
+ function assertReferencePhrase(value, name, span, sectionCount, nodeCount) {
4790
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4791
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4792
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4489
4793
  assertSpanOrder(startBeat, endBeat, name);
4490
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4794
+ _chunkS3QELJUEcjs.assertRange.call(void 0, startBeat, span.startBeat, span.endBeat, `${name}.startBeat`);
4795
+ _chunkS3QELJUEcjs.assertRange.call(void 0, endBeat, span.startBeat, span.endBeat, `${name}.endBeat`);
4796
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
4491
4797
  assertNullable(
4492
4798
  read.section,
4493
4799
  `${name}.section`,
4494
- (v, n) => _chunkS5BKZZGMcjs.assertInteger.call(void 0, v, n, 0, Math.max(sectionCount - 1, 0))
4800
+ (v, n) => _chunkS3QELJUEcjs.assertInteger.call(void 0, v, n, 0, Math.max(sectionCount - 1, 0))
4495
4801
  );
4496
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
4802
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
4497
4803
  assertNullable(read.cadence, `${name}.cadence`, assertReferenceCadence);
4498
4804
  assertNullable(read.melody, `${name}.melody`, assertReferencePhraseMelody);
4499
- const motifNodes = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.motifNodes, `${name}.motifNodes`);
4805
+ const motifNodes = _chunkS3QELJUEcjs.assertArray.call(void 0, read.motifNodes, `${name}.motifNodes`);
4500
4806
  for (let index = 0; index < motifNodes.length; index += 1) {
4501
- _chunkS5BKZZGMcjs.assertInteger.call(void 0,
4807
+ _chunkS3QELJUEcjs.assertInteger.call(void 0,
4502
4808
  motifNodes[index],
4503
4809
  `${name}.motifNodes[${index}]`,
4504
4810
  0,
@@ -4507,74 +4813,80 @@ function assertReferencePhrase(value, name, sectionCount, nodeCount) {
4507
4813
  }
4508
4814
  return read;
4509
4815
  }
4510
- function assertReferenceForm(value, name, nodeCount) {
4511
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4512
- const hypermeter = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
4816
+ function assertReferenceForm(value, name, span, nodeCount) {
4817
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4818
+ const hypermeter = _chunkS3QELJUEcjs.assertRecord.call(void 0,
4513
4819
  read.hypermeter,
4514
4820
  `${name}.hypermeter`
4515
4821
  );
4516
- _chunkS5BKZZGMcjs.assertRange.call(void 0, hypermeter.groupBars, 0, Number.MAX_SAFE_INTEGER, `${name}.hypermeter.groupBars`);
4517
- _chunkS5BKZZGMcjs.assertRange.call(void 0, hypermeter.confidence, 0, 1, `${name}.hypermeter.confidence`);
4518
- const sections = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.sections, `${name}.sections`);
4822
+ _chunkS3QELJUEcjs.assertRange.call(void 0, hypermeter.groupBars, 0, Number.MAX_SAFE_INTEGER, `${name}.hypermeter.groupBars`);
4823
+ _chunkS3QELJUEcjs.assertRange.call(void 0, hypermeter.confidence, 0, 1, `${name}.hypermeter.confidence`);
4824
+ const sections = _chunkS3QELJUEcjs.assertArray.call(void 0, read.sections, `${name}.sections`);
4519
4825
  for (let index = 0; index < sections.length; index += 1) {
4520
4826
  assertFormSection(sections[index], `${name}.sections[${index}]`, sections.length);
4521
4827
  }
4522
- const phrases = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.phrases, `${name}.phrases`);
4828
+ const phrases = _chunkS3QELJUEcjs.assertArray.call(void 0, read.phrases, `${name}.phrases`);
4523
4829
  for (let index = 0; index < phrases.length; index += 1) {
4524
- assertReferencePhrase(phrases[index], `${name}.phrases[${index}]`, sections.length, nodeCount);
4830
+ assertReferencePhrase(
4831
+ phrases[index],
4832
+ `${name}.phrases[${index}]`,
4833
+ span,
4834
+ sections.length,
4835
+ nodeCount
4836
+ );
4525
4837
  }
4526
4838
  return read;
4527
4839
  }
4528
4840
  function assertResolvedKeyField(value, name) {
4529
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4841
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4530
4842
  try {
4531
- _chunk3R3QVKY5cjs.resolveKey.call(void 0, read);
4843
+ _chunkYDGMWH43cjs.resolveKey.call(void 0, read);
4532
4844
  } catch (error) {
4533
- if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
4534
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be a valid key: ${error.message}`);
4845
+ if (error instanceof _chunkS3QELJUEcjs.InvalidInputError) {
4846
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must be a valid key: ${error.message}`);
4535
4847
  }
4536
4848
  throw error;
4537
4849
  }
4538
4850
  return read;
4539
4851
  }
4540
4852
  function assertReferenceKeyRegion(value, name) {
4541
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4542
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4543
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4853
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4854
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4855
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4544
4856
  assertSpanOrder(startBeat, endBeat, name);
4545
4857
  assertResolvedKeyField(read.key, `${name}.key`);
4546
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
4858
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
4547
4859
  return read;
4548
4860
  }
4549
4861
  function assertReferenceChord(value, name, keys) {
4550
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4551
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4552
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4862
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4863
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4864
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4553
4865
  assertSpanOrder(startBeat, endBeat, name);
4554
- const keyIndex = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.key, `${name}.key`, 0, Math.max(keys.length - 1, 0));
4866
+ const keyIndex = _chunkS3QELJUEcjs.assertInteger.call(void 0, read.key, `${name}.key`, 0, Math.max(keys.length - 1, 0));
4555
4867
  const roman = assertString(read.roman, `${name}.roman`);
4556
4868
  try {
4557
4869
  romanToChord(roman, keys[keyIndex]);
4558
4870
  } catch (error) {
4559
- if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
4560
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4561
- `${name}.roman must be readable by romanToChord; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, roman)}: ${error.message}`
4871
+ if (error instanceof _chunkS3QELJUEcjs.InvalidInputError) {
4872
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4873
+ `${name}.roman must be readable by romanToChord; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, roman)}: ${error.message}`
4562
4874
  );
4563
4875
  }
4564
4876
  throw error;
4565
4877
  }
4566
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.function, HARMONIC_FUNCTIONS, `${name}.function`);
4567
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.level, REDUCTION_LEVELS, `${name}.level`);
4878
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, read.function, HARMONIC_FUNCTIONS, `${name}.function`);
4879
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, read.level, REDUCTION_LEVELS, `${name}.level`);
4568
4880
  return read;
4569
4881
  }
4570
4882
  function assertReferenceHarmony(value, name) {
4571
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4572
- const keys = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.keys, `${name}.keys`);
4883
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4884
+ const keys = _chunkS3QELJUEcjs.assertArray.call(void 0, read.keys, `${name}.keys`);
4573
4885
  for (let index = 0; index < keys.length; index += 1) {
4574
4886
  assertReferenceKeyRegion(keys[index], `${name}.keys[${index}]`);
4575
4887
  }
4576
4888
  const resolvedKeys = keys.map((region) => region.key);
4577
- const chords = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.chords, `${name}.chords`);
4889
+ const chords = _chunkS3QELJUEcjs.assertArray.call(void 0, read.chords, `${name}.chords`);
4578
4890
  for (let index = 0; index < chords.length; index += 1) {
4579
4891
  assertReferenceChord(chords[index], `${name}.chords[${index}]`, resolvedKeys);
4580
4892
  }
@@ -4582,78 +4894,78 @@ function assertReferenceHarmony(value, name) {
4582
4894
  return read;
4583
4895
  }
4584
4896
  function assertMotifRelationSummary(value, name) {
4585
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4586
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.kind, MOTIF_RELATION_KINDS, `${name}.kind`);
4587
- _chunkS5BKZZGMcjs.assertFlag.call(void 0, read.sequence, `${name}.sequence`);
4588
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.semitones, `${name}.semitones`);
4897
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4898
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, read.kind, MOTIF_RELATION_KINDS, `${name}.kind`);
4899
+ _chunkS3QELJUEcjs.assertFlag.call(void 0, read.sequence, `${name}.sequence`);
4900
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.semitones, `${name}.semitones`);
4589
4901
  if (read.degrees !== void 0) {
4590
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.degrees, `${name}.degrees`);
4902
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.degrees, `${name}.degrees`);
4591
4903
  }
4592
- const timeRatio = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.timeRatio, `${name}.timeRatio`);
4904
+ const timeRatio = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.timeRatio, `${name}.timeRatio`);
4593
4905
  if (timeRatio <= 0) {
4594
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.timeRatio must be positive; received ${timeRatio}`);
4906
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}.timeRatio must be positive; received ${timeRatio}`);
4595
4907
  }
4596
4908
  return read;
4597
4909
  }
4598
4910
  function assertMotifGraphNode(value, name, occurrenceCounts) {
4599
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4600
- const motif = _chunkS5BKZZGMcjs.assertInteger.call(void 0,
4911
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4912
+ const motif = _chunkS3QELJUEcjs.assertInteger.call(void 0,
4601
4913
  read.motif,
4602
4914
  `${name}.motif`,
4603
4915
  0,
4604
4916
  Math.max(occurrenceCounts.length - 1, 0)
4605
4917
  );
4606
- _chunkS5BKZZGMcjs.assertInteger.call(void 0,
4918
+ _chunkS3QELJUEcjs.assertInteger.call(void 0,
4607
4919
  read.occurrence,
4608
4920
  `${name}.occurrence`,
4609
4921
  0,
4610
4922
  Math.max((_nullishCoalesce(occurrenceCounts[motif], () => ( 1))) - 1, 0)
4611
4923
  );
4612
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4613
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4924
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
4925
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
4614
4926
  assertSpanOrder(startBeat, endBeat, name);
4615
4927
  return read;
4616
4928
  }
4617
4929
  function assertMotifGraphEdge(value, name, nodeCount) {
4618
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4619
- const from = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.from, `${name}.from`, 0, Math.max(nodeCount - 1, 0));
4620
- const to = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.to, `${name}.to`, 0, Math.max(nodeCount - 1, 0));
4930
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4931
+ const from = _chunkS3QELJUEcjs.assertInteger.call(void 0, read.from, `${name}.from`, 0, Math.max(nodeCount - 1, 0));
4932
+ const to = _chunkS3QELJUEcjs.assertInteger.call(void 0, read.to, `${name}.to`, 0, Math.max(nodeCount - 1, 0));
4621
4933
  if (from >= to) {
4622
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4934
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4623
4935
  `${name}.from must be less than ${name}.to; received from=${from}, to=${to}`
4624
4936
  );
4625
4937
  }
4626
4938
  assertNullable(read.relation, `${name}.relation`, assertMotifRelationSummary);
4627
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
4939
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
4628
4940
  return read;
4629
4941
  }
4630
4942
  function assertMotifGraph(value, name, occurrenceCounts) {
4631
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4632
- const nodes = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.nodes, `${name}.nodes`);
4943
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4944
+ const nodes = _chunkS3QELJUEcjs.assertArray.call(void 0, read.nodes, `${name}.nodes`);
4633
4945
  for (let index = 0; index < nodes.length; index += 1) {
4634
4946
  assertMotifGraphNode(nodes[index], `${name}.nodes[${index}]`, occurrenceCounts);
4635
4947
  }
4636
- const edges = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.edges, `${name}.edges`);
4948
+ const edges = _chunkS3QELJUEcjs.assertArray.call(void 0, read.edges, `${name}.edges`);
4637
4949
  const seenTo = /* @__PURE__ */ new Set();
4638
4950
  for (let index = 0; index < edges.length; index += 1) {
4639
4951
  const edge = assertMotifGraphEdge(edges[index], `${name}.edges[${index}]`, nodes.length);
4640
4952
  if (seenTo.has(edge.to)) {
4641
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.edges: node ${edge.to} has more than one incoming edge`);
4953
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}.edges: node ${edge.to} has more than one incoming edge`);
4642
4954
  }
4643
4955
  seenTo.add(edge.to);
4644
4956
  }
4645
4957
  return read;
4646
4958
  }
4647
4959
  function assertReferenceMotif(value, name) {
4648
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
4649
- const intervals = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.intervals, `${name}.intervals`);
4960
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4961
+ const intervals = _chunkS3QELJUEcjs.assertArray.call(void 0, read.intervals, `${name}.intervals`);
4650
4962
  let pitch = 0;
4651
4963
  let low = 0;
4652
4964
  let high = 0;
4653
4965
  for (let index = 0; index < intervals.length; index += 1) {
4654
- const interval = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, intervals[index], `${name}.intervals[${index}]`);
4966
+ const interval = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, intervals[index], `${name}.intervals[${index}]`);
4655
4967
  if (!Number.isInteger(interval)) {
4656
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4968
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4657
4969
  `${name}.intervals[${index}] must be a whole number of semitones; received ${interval}`
4658
4970
  );
4659
4971
  }
@@ -4661,44 +4973,44 @@ function assertReferenceMotif(value, name) {
4661
4973
  low = Math.min(low, pitch);
4662
4974
  high = Math.max(high, pitch);
4663
4975
  }
4664
- if (high - low > 127) {
4665
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4666
- `${name}.intervals must span at most 127 semitones; received ${high - low}`
4976
+ if (high - low > MIDI_MAX) {
4977
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4978
+ `${name}.intervals must span at most ${MIDI_MAX} semitones; received ${high - low}`
4667
4979
  );
4668
4980
  }
4669
- const rhythm = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.rhythm, `${name}.rhythm`);
4981
+ const rhythm = _chunkS3QELJUEcjs.assertArray.call(void 0, read.rhythm, `${name}.rhythm`);
4670
4982
  if (rhythm.length !== intervals.length) {
4671
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4983
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4672
4984
  `${name}.rhythm must hold one ratio per interval (${intervals.length}); received ${rhythm.length}`
4673
4985
  );
4674
4986
  }
4675
4987
  for (let index = 0; index < rhythm.length; index += 1) {
4676
- const ratio2 = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, rhythm[index], `${name}.rhythm[${index}]`);
4988
+ const ratio2 = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, rhythm[index], `${name}.rhythm[${index}]`);
4677
4989
  if (!(ratio2 > 0)) {
4678
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.rhythm[${index}] must be positive; received ${ratio2}`);
4990
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}.rhythm[${index}] must be positive; received ${ratio2}`);
4679
4991
  }
4680
4992
  }
4681
- const unitBeats = _chunkS5BKZZGMcjs.assertRange.call(void 0, read.unitBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.unitBeats`);
4993
+ const unitBeats = _chunkS3QELJUEcjs.assertRange.call(void 0, read.unitBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.unitBeats`);
4682
4994
  if (unitBeats > 0 !== rhythm.length > 0) {
4683
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4995
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
4684
4996
  `${name}.unitBeats must be positive exactly when rhythm is non-empty; received ${unitBeats} with ${rhythm.length} ratio(s)`
4685
4997
  );
4686
4998
  }
4687
- _chunkS5BKZZGMcjs.assertRange.call(void 0, read.spanBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.spanBeats`);
4688
- _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, read.occurrences, `${name}.occurrences`);
4999
+ _chunkS3QELJUEcjs.assertRange.call(void 0, read.spanBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.spanBeats`);
5000
+ _chunkS3QELJUEcjs.assertPositiveInt.call(void 0, read.occurrences, `${name}.occurrences`);
4689
5001
  return read;
4690
5002
  }
4691
5003
  function assertReferenceMelody(value, name) {
4692
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
5004
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4693
5005
  assertNullable(read.register, `${name}.register`, (v, n) => {
4694
- const register = _chunkS5BKZZGMcjs.assertRecord.call(void 0, v, n);
4695
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, register.low, `${n}.low`);
4696
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, register.high, `${n}.high`);
4697
- _chunkS5BKZZGMcjs.assertRange.call(void 0, register.mean, 0, 127, `${n}.mean`);
5006
+ const register = _chunkS3QELJUEcjs.assertRecord.call(void 0, v, n);
5007
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, register.low, `${n}.low`);
5008
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, register.high, `${n}.high`);
5009
+ _chunkS3QELJUEcjs.assertRange.call(void 0, register.mean, 0, MIDI_MAX, `${n}.mean`);
4698
5010
  return register;
4699
5011
  });
4700
- assertNullable(read.shape, `${name}.shape`, (v, n) => _chunkS5BKZZGMcjs.assertOneOf.call(void 0, v, MELODIC_CONTOUR_SHAPES, n));
4701
- const motifs = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.motifs, `${name}.motifs`);
5012
+ assertNullable(read.shape, `${name}.shape`, (v, n) => _chunkS3QELJUEcjs.assertOneOf.call(void 0, v, MELODIC_CONTOUR_SHAPES, n));
5013
+ const motifs = _chunkS3QELJUEcjs.assertArray.call(void 0, read.motifs, `${name}.motifs`);
4702
5014
  const occurrenceCounts = [];
4703
5015
  for (let index = 0; index < motifs.length; index += 1) {
4704
5016
  assertReferenceMotif(motifs[index], `${name}.motifs[${index}]`);
@@ -4709,23 +5021,23 @@ function assertReferenceMelody(value, name) {
4709
5021
  return read;
4710
5022
  }
4711
5023
  function assertReferenceProfile(value, name = "reference profile") {
4712
- const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
5024
+ const read = _chunkS3QELJUEcjs.assertRecord.call(void 0, value, name);
4713
5025
  if (read.profileVersion !== REFERENCE_PROFILE_VERSION) {
4714
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4715
- `${name}.profileVersion must be ${REFERENCE_PROFILE_VERSION}; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, read.profileVersion)}`
5026
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
5027
+ `${name}.profileVersion must be ${REFERENCE_PROFILE_VERSION}; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, read.profileVersion)}`
4716
5028
  );
4717
5029
  }
4718
- const span = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
5030
+ const span = _chunkS3QELJUEcjs.assertRecord.call(void 0,
4719
5031
  read.span,
4720
5032
  `${name}.span`
4721
5033
  );
4722
- const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, span.startBeat, `${name}.span.startBeat`);
4723
- const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, span.endBeat, `${name}.span.endBeat`);
5034
+ const startBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, span.startBeat, `${name}.span.startBeat`);
5035
+ const endBeat = _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, span.endBeat, `${name}.span.endBeat`);
4724
5036
  assertSpanOrder(startBeat, endBeat, `${name}.span`);
4725
- _chunkS5BKZZGMcjs.assertRange.call(void 0, span.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.span.bars`);
4726
- _chunkS5BKZZGMcjs.assertMeterMap.call(void 0, read.meters, `${name}.meters`);
5037
+ _chunkS3QELJUEcjs.assertRange.call(void 0, span.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.span.bars`);
5038
+ _chunkS3QELJUEcjs.assertMeterMap.call(void 0, read.meters, `${name}.meters`);
4727
5039
  const melody = assertReferenceMelody(read.melody, `${name}.melody`);
4728
- assertReferenceForm(read.form, `${name}.form`, melody.graph.nodes.length);
5040
+ assertReferenceForm(read.form, `${name}.form`, { startBeat, endBeat }, melody.graph.nodes.length);
4729
5041
  assertReferenceHarmony(read.harmony, `${name}.harmony`);
4730
5042
  return read;
4731
5043
  }
@@ -4742,11 +5054,12 @@ var PEAK_POSITION_WEIGHT = 0.5;
4742
5054
  var FAMILY_SIZE_BINS = 5;
4743
5055
  var DEPTH_BINS = 4;
4744
5056
  var NAMED_CHORDS = ["Ger", "It", "Fr", "N"];
4745
- var PITCH_PRESERVING = /* @__PURE__ */ new Set([
5057
+ var PITCH_PRESERVING_KINDS = [
4746
5058
  "repetition",
4747
5059
  "augmentation",
4748
5060
  "diminution"
4749
- ]);
5061
+ ];
5062
+ var PITCH_PRESERVING = new Set(PITCH_PRESERVING_KINDS);
4750
5063
  var CELL_START_PITCH = 60;
4751
5064
  function whenBoth(hasA, hasB, measure) {
4752
5065
  if (!hasA && !hasB) {
@@ -4821,7 +5134,7 @@ function chordCost(x, y) {
4821
5134
  return x.function === y.function ? SAME_FUNCTION_COST : 1;
4822
5135
  }
4823
5136
  function progressionSimilarity(a, b, budget) {
4824
- _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, a.length * b.length, "reference progression alignment", budget);
5137
+ _chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, a.length * b.length, "reference progression alignment", budget);
4825
5138
  return alignedSimilarity(a, b, chordCost, PROGRESSION_GAP);
4826
5139
  }
4827
5140
  function functionMass(chords) {
@@ -4845,7 +5158,7 @@ function cadenceSimilarity(a, b) {
4845
5158
  });
4846
5159
  }
4847
5160
  function keyTokens(keys) {
4848
- const home = _nullishCoalesce(_optionalChain([keys, 'access', _115 => _115[0], 'optionalAccess', _116 => _116.scale, 'access', _117 => _117.rootPc]), () => ( 0));
5161
+ const home = _nullishCoalesce(_optionalChain([keys, 'access', _129 => _129[0], 'optionalAccess', _130 => _130.scale, 'access', _131 => _131.rootPc]), () => ( 0));
4849
5162
  return keys.map((key) => ({
4850
5163
  distance: ((key.scale.rootPc - home) % 12 + 12) % 12,
4851
5164
  mask: key.scale.modeMask12
@@ -4899,7 +5212,7 @@ function cellNotes(motif) {
4899
5212
  low = Math.min(low, offset);
4900
5213
  high = Math.max(high, offset);
4901
5214
  }
4902
- let pitch = Math.min(127 - high, Math.max(-low, CELL_START_PITCH));
5215
+ let pitch = Math.min(MIDI_MAX - high, Math.max(-low, CELL_START_PITCH));
4903
5216
  let beat = 0;
4904
5217
  for (let i = 0; i <= motif.intervals.length; i += 1) {
4905
5218
  const step = _nullishCoalesce(motif.rhythm[i], () => ( 1));
@@ -4955,7 +5268,7 @@ function summarise({ graph, spanBeats }) {
4955
5268
  const kinds = new Array(MOTIF_RELATION_KINDS.length + 1).fill(0);
4956
5269
  const parentEdge = /* @__PURE__ */ new Map();
4957
5270
  for (const edge of graph.edges) {
4958
- const kind = _optionalChain([edge, 'access', _118 => _118.relation, 'optionalAccess', _119 => _119.kind]);
5271
+ const kind = _optionalChain([edge, 'access', _132 => _132.relation, 'optionalAccess', _133 => _133.kind]);
4959
5272
  bump(
4960
5273
  kinds,
4961
5274
  kind === void 0 ? MOTIF_RELATION_KINDS.length : MOTIF_RELATION_KINDS.indexOf(kind)
@@ -4967,7 +5280,7 @@ function summarise({ graph, spanBeats }) {
4967
5280
  for (let node = 0; node < graph.nodes.length; node += 1) {
4968
5281
  const edge = parentEdge.get(node);
4969
5282
  if (edge !== void 0) {
4970
- const kind = _optionalChain([edge, 'access', _120 => _120.relation, 'optionalAccess', _121 => _121.kind]);
5283
+ const kind = _optionalChain([edge, 'access', _134 => _134.relation, 'optionalAccess', _135 => _135.kind]);
4971
5284
  const keepsPitch = kind !== void 0 && PITCH_PRESERVING.has(kind);
4972
5285
  depth[node] = (_nullishCoalesce(depth[edge.from], () => ( 0))) + (keepsPitch ? 0 : 1);
4973
5286
  root[node] = _nullishCoalesce(root[edge.from], () => ( edge.from));
@@ -5055,7 +5368,7 @@ function weightedMean(notes) {
5055
5368
  return total > 0 ? weighted / total : 0;
5056
5369
  }
5057
5370
  function phraseMelody(line, startBeat, endBeat, lineMean, meters, budget) {
5058
- const inside = line.filter((note) => note.startBeat >= startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS).map((note) => ({
5371
+ const inside = line.filter((note) => note.startBeat >= startBeat - _chunkS3QELJUEcjs.BEAT_EPS && note.startBeat < endBeat - _chunkS3QELJUEcjs.BEAT_EPS).map((note) => ({
5059
5372
  ...note,
5060
5373
  durationBeat: Math.min(note.startBeat + note.durationBeat, endBeat) - note.startBeat
5061
5374
  }));
@@ -5073,18 +5386,18 @@ function phraseMelody(line, startBeat, endBeat, lineMean, meters, budget) {
5073
5386
  }
5074
5387
  const length = endBeat - startBeat;
5075
5388
  const outline = [];
5076
- for (let k = 0; k < 8; k += 1) {
5077
- const at = startBeat + (k + 0.5) * length / 8;
5389
+ for (let k = 0; k < PHRASE_OUTLINE_POINTS; k += 1) {
5390
+ const at = startBeat + (k + 0.5) * length / PHRASE_OUTLINE_POINTS;
5078
5391
  let held = first;
5079
5392
  for (const note of inside) {
5080
- if (note.startBeat > at + _chunkS5BKZZGMcjs.BEAT_EPS) {
5393
+ if (note.startBeat > at + _chunkS3QELJUEcjs.BEAT_EPS) {
5081
5394
  break;
5082
5395
  }
5083
5396
  held = note;
5084
5397
  }
5085
5398
  outline.push(held.pitch - lineMean);
5086
5399
  }
5087
- const rhythm = analyzeRhythm(inside, { meters, totalBeats: endBeat, budget });
5400
+ const rhythm = rhythmOver(inside, { meters, totalBeats: endBeat, budget }, startBeat);
5088
5401
  return {
5089
5402
  shape: melodicContour(inside).shape,
5090
5403
  low,
@@ -5191,5 +5504,10 @@ function phraseMelody(line, startBeat, endBeat, lineMean, meters, budget) {
5191
5504
 
5192
5505
 
5193
5506
 
5194
- exports.HUMANIZE_ADJACENCY = HUMANIZE_ADJACENCY; exports.sameInstant = sameInstant; exports.adjacent = adjacent; exports.hasEnded = hasEnded; exports.windowWeights = windowWeights; exports.MODAL_SCALE_NAMES = MODAL_SCALE_NAMES; exports.detectChord = detectChord; exports.detectChordBest = detectChordBest; exports.detectKey = detectKey; exports.detectKeyFromNotes = detectKeyFromNotes; exports.detectKeyBest = detectKeyBest; exports.augmentedSixthChord = augmentedSixthChord; exports.augmentedSixthKind = augmentedSixthKind; exports.augmentedSixthFromPitchClasses = augmentedSixthFromPitchClasses; exports.spellAugmentedSixth = spellAugmentedSixth; exports.CADENCE_TYPES = CADENCE_TYPES; exports.detectCadence = detectCadence; exports.isBorrowedChord = isBorrowedChord; exports.borrowedSource = borrowedSource; exports.romanToChord = romanToChord; exports.chordToRoman = chordToRoman; exports.explainRoman = explainRoman; exports.HARMONIC_FUNCTIONS = HARMONIC_FUNCTIONS; exports.isMinorKey = isMinorKey; exports.functionOf = functionOf; exports.isDiatonic = isDiatonic; exports.parallelKey = parallelKey; exports.analyzeChord = analyzeChord; exports.secondaryDominant = secondaryDominant; exports.secondaryDominantOf = secondaryDominantOf; exports.pivotChords = pivotChords; exports.barSpanOf = barSpanOf; exports.clamp01 = clamp01; exports.floorMod = floorMod; exports.firstSoundingBeat = firstSoundingBeat; exports.lastBarOf = lastBarOf; exports.sliceBars = sliceBars; exports.weightSimilarity = weightSimilarity; exports.harmonicNovelty = harmonicNovelty; exports.combineEvidence = combineEvidence; exports.gridForNotes = gridForNotes; exports.barGridStart = barGridStart; exports.keyTimelineFromNotes = keyTimelineFromNotes; exports.detectModulations = detectModulations; exports.attachPivots = attachPivots; exports.keyLookup = keyLookup; exports.prevailingKeyOf = prevailingKeyOf; exports.toVoiceNotes = toVoiceNotes; exports.analyzeVoice = analyzeVoice; exports.assertChordTimeline = assertChordTimeline; exports.chordTimelineFromChords = chordTimelineFromChords; exports.chordAtBeat = chordAtBeat; exports.segmentStartingAt = segmentStartingAt; exports.chordTimelineFromNotes = chordTimelineFromNotes; exports.dirtySlotsFor = dirtySlotsFor; exports.analyzeTimeline = analyzeTimeline; exports.detectCadences = detectCadences; exports.orderedNotes = orderedNotes; exports.MELODIC_CONTOUR_SHAPES = MELODIC_CONTOUR_SHAPES; exports.melodicContour = melodicContour; exports.extractMotifs = extractMotifs; exports.motifFromNotes = motifFromNotes; exports.MOTIF_RELATION_KINDS = MOTIF_RELATION_KINDS; exports.relateMotifs = relateMotifs; exports.compareMelodies = compareMelodies; exports.melodicSimilarity = melodicSimilarity; exports.DEFAULT_VARIATION_THRESHOLD = DEFAULT_VARIATION_THRESHOLD; exports.motifGraph = motifGraph; exports.REDUCTION_LEVELS = REDUCTION_LEVELS; exports.reduceProgression = reduceProgression; exports.sectionSequenceSimilarity = sectionSequenceSimilarity; exports.phraseLengthSimilarity = phraseLengthSimilarity; exports.progressionSimilarity = progressionSimilarity; exports.functionSimilarity = functionSimilarity; exports.cadenceSimilarity = cadenceSimilarity; exports.keyPlanSimilarity = keyPlanSimilarity; exports.onsetSimilarity = onsetSimilarity; exports.densitySimilarity = densitySimilarity; exports.durationSimilarity = durationSimilarity; exports.syncopationSimilarity = syncopationSimilarity; exports.surfaceSimilarity = surfaceSimilarity; exports.motifStructureSimilarity = motifStructureSimilarity; exports.contourSimilarity = contourSimilarity; exports.registerSimilarity = registerSimilarity; exports.rhythmLevel = rhythmLevel; exports.ioiBinIndex = ioiBinIndex; exports.RHYTHM_IOI_BINS = RHYTHM_IOI_BINS; exports.analyzeRhythm = analyzeRhythm; exports.REFERENCE_PROFILE_VERSION = REFERENCE_PROFILE_VERSION; exports.assertReferenceProfile = assertReferenceProfile; exports.weightedMean = weightedMean; exports.phraseMelody = phraseMelody;
5195
- //# sourceMappingURL=chunk-IFZDWIKM.cjs.map
5507
+
5508
+
5509
+
5510
+
5511
+
5512
+ exports.HUMANIZE_ADJACENCY = HUMANIZE_ADJACENCY; exports.sameInstant = sameInstant; exports.adjacent = adjacent; exports.hasEnded = hasEnded; exports.windowWeights = windowWeights; exports.MODAL_SCALE_NAMES = MODAL_SCALE_NAMES; exports.detectChord = detectChord; exports.detectChordBest = detectChordBest; exports.detectKey = detectKey; exports.detectKeyFromNotes = detectKeyFromNotes; exports.detectKeyBest = detectKeyBest; exports.barSpanOf = barSpanOf; exports.clamp01 = clamp01; exports.floorMod = floorMod; exports.firstSoundingBeat = firstSoundingBeat; exports.resolveSpanEnd = resolveSpanEnd; exports.lastBarOf = lastBarOf; exports.sliceBars = sliceBars; exports.weightSimilarity = weightSimilarity; exports.harmonicNovelty = harmonicNovelty; exports.combineEvidence = combineEvidence; exports.augmentedSixthChord = augmentedSixthChord; exports.augmentedSixthKind = augmentedSixthKind; exports.augmentedSixthFromPitchClasses = augmentedSixthFromPitchClasses; exports.spellAugmentedSixth = spellAugmentedSixth; exports.CADENCE_TYPES = CADENCE_TYPES; exports.detectCadence = detectCadence; exports.isBorrowedChord = isBorrowedChord; exports.borrowedSource = borrowedSource; exports.romanToChord = romanToChord; exports.chordToRoman = chordToRoman; exports.explainRoman = explainRoman; exports.HARMONIC_FUNCTIONS = HARMONIC_FUNCTIONS; exports.isMinorKey = isMinorKey; exports.functionOf = functionOf; exports.isDiatonic = isDiatonic; exports.parallelKey = parallelKey; exports.analyzeChord = analyzeChord; exports.secondaryDominant = secondaryDominant; exports.secondaryDominantOf = secondaryDominantOf; exports.pivotChords = pivotChords; exports.gridForNotes = gridForNotes; exports.latestSoundingEnd = latestSoundingEnd; exports.barGridStart = barGridStart; exports.keyTimelineFromNotes = keyTimelineFromNotes; exports.detectModulations = detectModulations; exports.attachPivots = attachPivots; exports.keyLookup = keyLookup; exports.prevailingKeyOf = prevailingKeyOf; exports.toVoiceNotes = toVoiceNotes; exports.analyzeVoice = analyzeVoice; exports.assertChordTimeline = assertChordTimeline; exports.assertSpansInOrder = assertSpansInOrder; exports.statedKeyRegions = statedKeyRegions; exports.chordTimelineFromChords = chordTimelineFromChords; exports.chordAtBeat = chordAtBeat; exports.segmentStartingAt = segmentStartingAt; exports.chordTimelineFromNotes = chordTimelineFromNotes; exports.dirtySlotsFor = dirtySlotsFor; exports.analyzeTimeline = analyzeTimeline; exports.detectCadences = detectCadences; exports.orderedNotes = orderedNotes; exports.MELODIC_CONTOUR_SHAPES = MELODIC_CONTOUR_SHAPES; exports.melodicContour = melodicContour; exports.extractMotifs = extractMotifs; exports.motifFromNotes = motifFromNotes; exports.MOTIF_RELATION_KINDS = MOTIF_RELATION_KINDS; exports.relateMotifs = relateMotifs; exports.compareMelodies = compareMelodies; exports.melodicSimilarity = melodicSimilarity; exports.DEFAULT_VARIATION_THRESHOLD = DEFAULT_VARIATION_THRESHOLD; exports.motifGraph = motifGraph; exports.REDUCTION_LEVELS = REDUCTION_LEVELS; exports.reduceProgression = reduceProgression; exports.REFERENCE_PROFILE_VERSION = REFERENCE_PROFILE_VERSION; exports.sectionSequenceSimilarity = sectionSequenceSimilarity; exports.phraseLengthSimilarity = phraseLengthSimilarity; exports.progressionSimilarity = progressionSimilarity; exports.functionSimilarity = functionSimilarity; exports.cadenceSimilarity = cadenceSimilarity; exports.keyPlanSimilarity = keyPlanSimilarity; exports.onsetSimilarity = onsetSimilarity; exports.densitySimilarity = densitySimilarity; exports.durationSimilarity = durationSimilarity; exports.syncopationSimilarity = syncopationSimilarity; exports.surfaceSimilarity = surfaceSimilarity; exports.motifStructureSimilarity = motifStructureSimilarity; exports.contourSimilarity = contourSimilarity; exports.registerSimilarity = registerSimilarity; exports.rhythmLevel = rhythmLevel; exports.ioiBinIndex = ioiBinIndex; exports.RHYTHM_IOI_BINS = RHYTHM_IOI_BINS; exports.analyzeRhythm = analyzeRhythm; exports.rhythmOver = rhythmOver; exports.assertReferenceProfile = assertReferenceProfile; exports.weightedMean = weightedMean; exports.phraseMelody = phraseMelody;
5513
+ //# sourceMappingURL=chunk-LQZQDBCD.cjs.map