@libraz/libcantus 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +27 -65
  2. package/README_ja.md +28 -66
  3. package/dist/analyze/index.cjs +11 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +79 -144
  6. package/dist/analyze/index.d.ts +79 -144
  7. package/dist/analyze/index.js +20 -16
  8. package/dist/chunk-35NBV2FT.cjs +145 -0
  9. package/dist/chunk-35NBV2FT.cjs.map +1 -0
  10. package/dist/{chunk-UADYAYAT.cjs → chunk-3R3QVKY5.cjs} +1353 -580
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-WWURE4XT.cjs → chunk-76ZBTRG2.cjs} +624 -729
  13. package/dist/chunk-76ZBTRG2.cjs.map +1 -0
  14. package/dist/{chunk-FVSI36UV.js → chunk-7KG3RTYS.js} +197 -110
  15. package/dist/chunk-7KG3RTYS.js.map +1 -0
  16. package/dist/{chunk-VTI6FOLB.js → chunk-AGOVW7U6.js} +709 -133
  17. package/dist/chunk-AGOVW7U6.js.map +1 -0
  18. package/dist/{chunk-E3F4AHAH.cjs → chunk-CGBADIO5.cjs} +221 -134
  19. package/dist/chunk-CGBADIO5.cjs.map +1 -0
  20. package/dist/{chunk-6XH6QZYQ.cjs → chunk-DSMJRXCZ.cjs} +1455 -849
  21. package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
  22. package/dist/{chunk-PKFNUHDY.js → chunk-FBH75DAY.js} +1218 -612
  23. package/dist/chunk-FBH75DAY.js.map +1 -0
  24. package/dist/chunk-H2KJHBW4.js +3329 -0
  25. package/dist/chunk-H2KJHBW4.js.map +1 -0
  26. package/dist/chunk-HPVSH7SF.cjs +2294 -0
  27. package/dist/chunk-HPVSH7SF.cjs.map +1 -0
  28. package/dist/{chunk-MXO72PLY.cjs → chunk-HWHGOVO6.cjs} +21 -19
  29. package/dist/chunk-HWHGOVO6.cjs.map +1 -0
  30. package/dist/chunk-HYJJ2V2I.js +2294 -0
  31. package/dist/chunk-HYJJ2V2I.js.map +1 -0
  32. package/dist/{chunk-AFM2ULJH.cjs → chunk-J7FHVZII.cjs} +1567 -1067
  33. package/dist/chunk-J7FHVZII.cjs.map +1 -0
  34. package/dist/chunk-K6OZXIR6.js +1 -0
  35. package/dist/{chunk-H3VORETI.js → chunk-M5RYMAFI.js} +487 -592
  36. package/dist/chunk-M5RYMAFI.js.map +1 -0
  37. package/dist/chunk-NB25P3EH.js +363 -0
  38. package/dist/chunk-NB25P3EH.js.map +1 -0
  39. package/dist/{chunk-TTCLJX6D.js → chunk-NFJ4P6ZZ.js} +10 -8
  40. package/dist/chunk-NFJ4P6ZZ.js.map +1 -0
  41. package/dist/chunk-NM7UAMC3.cjs +457 -0
  42. package/dist/chunk-NM7UAMC3.cjs.map +1 -0
  43. package/dist/chunk-PM4XJE2Y.cjs +363 -0
  44. package/dist/chunk-PM4XJE2Y.cjs.map +1 -0
  45. package/dist/chunk-PUKJP2Z5.js +145 -0
  46. package/dist/chunk-PUKJP2Z5.js.map +1 -0
  47. package/dist/{chunk-VOHZB45S.js → chunk-Q6LFSAKV.js} +1321 -821
  48. package/dist/chunk-Q6LFSAKV.js.map +1 -0
  49. package/dist/{chunk-INHVXP6Z.js → chunk-RFIBF2RR.js} +1203 -430
  50. package/dist/chunk-RFIBF2RR.js.map +1 -0
  51. package/dist/{chunk-Q2WGKJNN.cjs → chunk-S5BKZZGM.cjs} +699 -123
  52. package/dist/chunk-S5BKZZGM.cjs.map +1 -0
  53. package/dist/chunk-SGLEQVJH.cjs +3329 -0
  54. package/dist/chunk-SGLEQVJH.cjs.map +1 -0
  55. package/dist/chunk-UGLFDG7L.cjs +1 -0
  56. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  57. package/dist/{chunk-YH3BIKHH.js → chunk-UMWZ4ZET.js} +146 -49
  58. package/dist/chunk-UMWZ4ZET.js.map +1 -0
  59. package/dist/{index-szBv3496.d.ts → coerce-DP0vYu-x.d.ts} +286 -199
  60. package/dist/{index-C_8o--VE.d.cts → coerce-DRERzdve.d.cts} +286 -199
  61. package/dist/core/index.cjs +9 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +122 -108
  64. package/dist/core/index.d.ts +122 -108
  65. package/dist/core/index.js +32 -28
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.d.cts +168 -84
  68. package/dist/generate/index.d.ts +168 -84
  69. package/dist/generate/index.js +7 -7
  70. package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
  71. package/dist/{index-CCNJRG-P.d.cts → index-B9QLziDe.d.cts} +293 -56
  72. package/dist/{index-Dzr13q3d.d.cts → index-BBWNZS0f.d.cts} +135 -58
  73. package/dist/{index-9wcMhG73.d.ts → index-BHIUyjhZ.d.ts} +293 -56
  74. package/dist/{index-BGFW-8P2.d.cts → index-BJgP97N1.d.ts} +196 -114
  75. package/dist/{index-CunYKLiu.d.ts → index-BL5N2Oyz.d.cts} +1 -1
  76. package/dist/{index-Cj49F892.d.ts → index-BMweUlm5.d.cts} +63 -23
  77. package/dist/{index-CgHqxdps.d.ts → index-BNSM-DSk.d.cts} +196 -114
  78. package/dist/{index-D57QRbVn.d.ts → index-BRMbPkHE.d.ts} +480 -254
  79. package/dist/{index-Chbst3Jx.d.ts → index-CJBBiAmU.d.ts} +135 -58
  80. package/dist/{index-BkGmlyI3.d.cts → index-CL4b4gUQ.d.ts} +63 -23
  81. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  82. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  83. package/dist/{index-BO6xE9YU.d.cts → index-DGCNKAUr.d.cts} +480 -254
  84. package/dist/{index-B4o55NDr.d.cts → index-Dw2DP7vm.d.ts} +1 -1
  85. package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
  86. package/dist/index.cjs +42 -14
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +14 -14
  89. package/dist/index.d.ts +14 -14
  90. package/dist/index.js +82 -54
  91. package/dist/{masks-DYbE485J.d.cts → masks-Cc7-MzzY.d.ts} +49 -2
  92. package/dist/{masks-DYbE485J.d.ts → masks-pADfTOiD.d.cts} +49 -2
  93. package/dist/model/index.cjs +19 -12
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +504 -182
  96. package/dist/model/index.d.ts +504 -182
  97. package/dist/model/index.js +18 -11
  98. package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
  99. package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
  100. package/dist/theory/index.cjs +35 -6
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +257 -62
  103. package/dist/theory/index.d.ts +257 -62
  104. package/dist/theory/index.js +41 -12
  105. package/package.json +7 -5
  106. package/dist/chunk-2LCPM3EG.js +0 -3224
  107. package/dist/chunk-2LCPM3EG.js.map +0 -1
  108. package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
  109. package/dist/chunk-6XPTBE7Y.cjs +0 -49
  110. package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
  111. package/dist/chunk-AFM2ULJH.cjs.map +0 -1
  112. package/dist/chunk-B6XNKWZV.cjs +0 -3224
  113. package/dist/chunk-B6XNKWZV.cjs.map +0 -1
  114. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  115. package/dist/chunk-ELCTZUJJ.js +0 -379
  116. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  117. package/dist/chunk-FVSI36UV.js.map +0 -1
  118. package/dist/chunk-H3VORETI.js.map +0 -1
  119. package/dist/chunk-H42UHPYW.js +0 -1577
  120. package/dist/chunk-H42UHPYW.js.map +0 -1
  121. package/dist/chunk-HFPS7JFD.js +0 -1
  122. package/dist/chunk-INHVXP6Z.js.map +0 -1
  123. package/dist/chunk-K4ZJUGF4.cjs +0 -379
  124. package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
  125. package/dist/chunk-KNJEKLKV.js +0 -49
  126. package/dist/chunk-KNJEKLKV.js.map +0 -1
  127. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  128. package/dist/chunk-NLEU4QSE.cjs +0 -360
  129. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  130. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  131. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  132. package/dist/chunk-PKFNUHDY.js.map +0 -1
  133. package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
  134. package/dist/chunk-RHIJ3S4I.cjs +0 -1
  135. package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
  136. package/dist/chunk-TTCLJX6D.js.map +0 -1
  137. package/dist/chunk-UADYAYAT.cjs.map +0 -1
  138. package/dist/chunk-VOHZB45S.js.map +0 -1
  139. package/dist/chunk-VTI6FOLB.js.map +0 -1
  140. package/dist/chunk-WWURE4XT.cjs.map +0 -1
  141. package/dist/chunk-YH3BIKHH.js.map +0 -1
  142. /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
@@ -5,10 +5,10 @@
5
5
 
6
6
 
7
7
 
8
- var _chunkOE6WK2Y2cjs = require('./chunk-OE6WK2Y2.cjs');
9
8
 
10
9
 
11
10
 
11
+ var _chunkSGLEQVJHcjs = require('./chunk-SGLEQVJH.cjs');
12
12
 
13
13
 
14
14
 
@@ -16,18 +16,18 @@ var _chunkOE6WK2Y2cjs = require('./chunk-OE6WK2Y2.cjs');
16
16
 
17
17
 
18
18
 
19
- var _chunkE3F4AHAHcjs = require('./chunk-E3F4AHAH.cjs');
20
19
 
21
20
 
22
21
 
22
+ var _chunkCGBADIO5cjs = require('./chunk-CGBADIO5.cjs');
23
23
 
24
24
 
25
+ var _chunk35NBV2FTcjs = require('./chunk-35NBV2FT.cjs');
25
26
 
26
27
 
27
- var _chunkK4ZJUGF4cjs = require('./chunk-K4ZJUGF4.cjs');
28
28
 
29
+ var _chunkPM4XJE2Ycjs = require('./chunk-PM4XJE2Y.cjs');
29
30
 
30
- var _chunk6XPTBE7Ycjs = require('./chunk-6XPTBE7Y.cjs');
31
31
 
32
32
 
33
33
 
@@ -51,7 +51,6 @@ var _chunk6XPTBE7Ycjs = require('./chunk-6XPTBE7Y.cjs');
51
51
 
52
52
 
53
53
 
54
- var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
55
54
 
56
55
 
57
56
 
@@ -59,6 +58,7 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
59
58
 
60
59
 
61
60
 
61
+ var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
62
62
 
63
63
 
64
64
 
@@ -71,7 +71,89 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
71
71
 
72
72
 
73
73
 
74
- var _chunkQ2WGKJNNcjs = require('./chunk-Q2WGKJNN.cjs');
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+ var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
99
+
100
+ // src/generate/context/draw.ts
101
+ var DRAW_METHODS = ["at", "prob", "range", "float"];
102
+ function assertDraw(draw, name = "draw") {
103
+ const sampler = _chunkS5BKZZGMcjs.assertRecord.call(void 0, draw, name);
104
+ for (const method of DRAW_METHODS) {
105
+ _chunkS5BKZZGMcjs.assertFunction.call(void 0, sampler[method], `${name}.${method}`);
106
+ }
107
+ return sampler;
108
+ }
109
+ function assertSeedPath(path, name = "draw path") {
110
+ const steps = _chunkS5BKZZGMcjs.assertArray.call(void 0, path, name);
111
+ for (let index = 0; index < steps.length; index += 1) {
112
+ const step = steps[index];
113
+ if (typeof step !== "string" && !Number.isFinite(step)) {
114
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
115
+ `${name}[${index}] must be a string or a finite number; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, step)}`
116
+ );
117
+ }
118
+ }
119
+ return steps;
120
+ }
121
+ function drawFrom(rng, path) {
122
+ const draw = rng.at(...path);
123
+ if (!(draw >= 0 && draw < 1)) {
124
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`positional draw must be in [0, 1); received ${draw}`);
125
+ }
126
+ return draw;
127
+ }
128
+ function drawsFrom(rng, ...prefix) {
129
+ const at2 = (...path) => drawFrom(rng, [...prefix, ...path]);
130
+ return {
131
+ at: at2,
132
+ prob: (p, ...path) => _chunkCGBADIO5cjs.includeAt.call(void 0, rng, p, ...prefix, ...path),
133
+ range: (lo, hi, ...path) => {
134
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, lo, "range lower bound");
135
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, hi, "range upper bound");
136
+ if (lo > hi) {
137
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
138
+ `range lower bound must not exceed upper bound; received ${lo} > ${hi}`
139
+ );
140
+ }
141
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, hi - lo, "range span", 0);
142
+ return lo + Math.floor(at2(...path) * (hi - lo + 1));
143
+ },
144
+ float: (lo, hi, ...path) => {
145
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, lo, "float lower bound");
146
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, hi, "float upper bound");
147
+ if (lo > hi) {
148
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
149
+ `float lower bound must not exceed upper bound; received ${lo} > ${hi}`
150
+ );
151
+ }
152
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, hi - lo, "float span");
153
+ return lo + at2(...path) * (hi - lo);
154
+ }
155
+ };
156
+ }
75
157
 
76
158
  // src/generate/drums/internal.ts
77
159
  var GM = {
@@ -115,6 +197,16 @@ var PUBLIC_SECTIONS = [
115
197
  "bridge",
116
198
  "outro"
117
199
  ];
200
+ function backbeatBeats(snareBeat3, barBeats = 4) {
201
+ if (snareBeat3) {
202
+ return barBeats > 2 ? [2] : [];
203
+ }
204
+ const beats = [];
205
+ for (let beat = 1; beat < barBeats; beat += 2) {
206
+ beats.push(beat);
207
+ }
208
+ return beats;
209
+ }
118
210
  var STYLE_MAP = {
119
211
  // Standard pop pulse: kick on beats 1 and 3, backbeat snare on 2 and 4.
120
212
  standard: { style: "standard", feel: "straight", ghostBoost: false, snareBeat3: false },
@@ -286,50 +378,59 @@ function sameTs(a, b) {
286
378
  return a.numerator === b.numerator && a.denominator === b.denominator;
287
379
  }
288
380
  function fitsQuery(entry, query) {
289
- if (query.genre !== void 0 && entry.genre !== query.genre) {
381
+ const candidate = _chunkS5BKZZGMcjs.assertRecord.call(void 0, entry, "vocabulary entry");
382
+ const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, query, "vocabulary query");
383
+ if (asked.genre !== void 0 && candidate.genre !== asked.genre) {
290
384
  return false;
291
385
  }
292
- if (query.section !== void 0 && entry.sections && !entry.sections.includes(query.section)) {
386
+ if (asked.section !== void 0 && candidate.sections && !candidate.sections.includes(asked.section)) {
293
387
  return false;
294
388
  }
295
- if (query.bpm !== void 0 && entry.tempoRange) {
296
- const [low, high] = entry.tempoRange;
297
- if (query.bpm < low || query.bpm > high) {
389
+ if (asked.bpm !== void 0 && candidate.tempoRange) {
390
+ const [low, high] = candidate.tempoRange;
391
+ if (asked.bpm < low || asked.bpm > high) {
298
392
  return false;
299
393
  }
300
394
  }
301
- if (query.ts !== void 0 && entry.ts && !sameTs(entry.ts, query.ts)) {
395
+ if (asked.ts !== void 0 && candidate.ts && !sameTs(candidate.ts, _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, asked.ts, "ts")))) {
302
396
  return false;
303
397
  }
304
- if (query.quality !== void 0 && entry.fitsOver && !entry.fitsOver.includes(query.quality)) {
398
+ if (asked.quality !== void 0 && candidate.fitsOver && !candidate.fitsOver.includes(asked.quality)) {
305
399
  return false;
306
400
  }
307
- if (query.articulations !== void 0) {
308
- const available = query.articulations;
309
- if (entry.articulations.some((articulation) => !available.includes(articulation))) {
401
+ if (asked.articulations !== void 0) {
402
+ const available = asked.articulations;
403
+ if (candidate.articulations.some((articulation) => !available.includes(articulation))) {
310
404
  return false;
311
405
  }
312
406
  }
313
- return query.difficulty === void 0 || entry.difficulty <= query.difficulty;
407
+ return asked.difficulty === void 0 || candidate.difficulty <= asked.difficulty;
314
408
  }
315
409
  function selectVocabulary(dictionary, query) {
316
- return dictionary.filter((entry) => fitsQuery(entry, query));
410
+ return _chunkS5BKZZGMcjs.assertArray.call(void 0, dictionary, "dictionary").filter(
411
+ (entry) => fitsQuery(entry, query)
412
+ );
317
413
  }
318
414
  function pickVocabulary(dictionary, query, draw, ...path) {
415
+ const sampler = assertDraw(draw);
416
+ assertSeedPath(path);
319
417
  const candidates = selectVocabulary(dictionary, query);
320
418
  if (candidates.length === 0) {
321
419
  return void 0;
322
420
  }
323
- return candidates[draw.range(0, candidates.length - 1, ...path)];
421
+ return candidates[sampler.range(0, candidates.length - 1, ...path)];
324
422
  }
325
423
  function mergeVocabulary(builtIn, supplied) {
326
- if (!supplied || supplied.length === 0) {
327
- return [...builtIn];
328
- }
329
- const byId = new Map(supplied.map((entry) => [entry.id, entry]));
330
- const merged = builtIn.map((entry) => _nullishCoalesce(byId.get(entry.id), () => ( entry)));
331
- const replaced = new Set(builtIn.map((entry) => entry.id));
332
- for (const entry of supplied) {
424
+ const library = _chunkS5BKZZGMcjs.assertArray.call(void 0, builtIn, "builtIn");
425
+ const extra = supplied === void 0 || supplied === null ? [] : _chunkS5BKZZGMcjs.assertArray.call(void 0, supplied, "supplied");
426
+ if (extra.length === 0) {
427
+ return [...library];
428
+ }
429
+ const idOf = (entry, index, side) => _chunkS5BKZZGMcjs.assertRecord.call(void 0, entry, `${side}[${index}]`).id;
430
+ const byId = new Map(extra.map((entry, index) => [idOf(entry, index, "supplied"), entry]));
431
+ const merged = library.map((entry, index) => _nullishCoalesce(byId.get(idOf(entry, index, "builtIn")), () => ( entry)));
432
+ const replaced = new Set(library.map((entry, index) => idOf(entry, index, "builtIn")));
433
+ for (const entry of extra) {
333
434
  if (!replaced.has(entry.id)) {
334
435
  merged.push(entry);
335
436
  }
@@ -338,33 +439,52 @@ function mergeVocabulary(builtIn, supplied) {
338
439
  }
339
440
  function vocabularyOfKind(entries, isMaterial) {
340
441
  const out = [];
341
- for (const entry of entries) {
342
- if (isMaterial(entry.material)) {
442
+ const recognises = _chunkS5BKZZGMcjs.assertFunction.call(void 0, isMaterial, "isMaterial");
443
+ for (const entry of _chunkS5BKZZGMcjs.assertArray.call(void 0, entries, "entries")) {
444
+ if (recognises(_chunkS5BKZZGMcjs.assertRecord.call(void 0, entry, "entry").material)) {
343
445
  out.push(entry);
344
446
  }
345
447
  }
346
448
  return out;
347
449
  }
348
- function assertVocabulary(entry, label = "vocabulary entry") {
349
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, entry.genre, GENRES, `${label} genre`);
350
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _optionalChain([entry, 'access', _2 => _2.provenance, 'optionalAccess', _3 => _3.basis]), PROVENANCE_BASES, `${label} provenance basis`);
351
- entry.articulations.forEach((articulation, index) => {
352
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, articulation, _chunkE3F4AHAHcjs.ARTICULATIONS, `${label} articulations[${index}]`);
450
+ function assertNames(values, allowed, label) {
451
+ if (!Array.isArray(values)) {
452
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
453
+ `${label} must be an array of names; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, values)}`
454
+ );
455
+ }
456
+ values.forEach((value, index) => {
457
+ _chunkS5BKZZGMcjs.assertOneOf.call(void 0, value, allowed, `${label}[${index}]`);
353
458
  });
354
- _optionalChain([entry, 'access', _4 => _4.sections, 'optionalAccess', _5 => _5.forEach, 'call', _6 => _6((section, index) => {
355
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, section, PUBLIC_SECTIONS, `${label} sections[${index}]`);
356
- })]);
357
- _optionalChain([entry, 'access', _7 => _7.fitsOver, 'optionalAccess', _8 => _8.forEach, 'call', _9 => _9((quality, index) => {
358
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, quality, _chunkUADYAYATcjs.chordQualities.call(void 0, ), `${label} fitsOver[${index}]`);
359
- })]);
459
+ }
460
+ function assertVocabulary(entry, label = "vocabulary entry") {
461
+ if (typeof entry !== "object" || entry === null) {
462
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
463
+ `${label} must be a dictionary entry; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, entry)}`
464
+ );
465
+ }
466
+ _chunkS5BKZZGMcjs.assertOneOf.call(void 0, entry.genre, GENRES, `${label} genre`);
467
+ _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _optionalChain([entry, 'access', _2 => _2.provenance, 'optionalAccess', _3 => _3.basis]), PROVENANCE_BASES, `${label} provenance basis`);
468
+ assertNames(entry.articulations, _chunkS5BKZZGMcjs.ARTICULATIONS, `${label} articulations`);
469
+ if (entry.sections !== void 0) {
470
+ assertNames(entry.sections, PUBLIC_SECTIONS, `${label} sections`);
471
+ }
472
+ if (entry.fitsOver !== void 0) {
473
+ assertNames(entry.fitsOver, _chunk3R3QVKY5cjs.chordQualities.call(void 0, ), `${label} fitsOver`);
474
+ }
360
475
  if (entry.ts !== void 0) {
361
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, entry.ts, `${label} ts`);
476
+ _chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, entry.ts, `${label} ts`);
362
477
  }
363
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, entry.difficulty, 1, 5, `${label} difficulty`);
364
- if (entry.tempoRange) {
478
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, entry.difficulty, 1, 5, `${label} difficulty`);
479
+ if (entry.tempoRange !== void 0) {
480
+ if (!Array.isArray(entry.tempoRange) || entry.tempoRange.length !== 2) {
481
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
482
+ `${label} tempoRange must be a low and a high tempo; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, entry.tempoRange)}`
483
+ );
484
+ }
365
485
  const [low, high] = entry.tempoRange;
366
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, low, Number.MIN_VALUE, 1e3, `${label} tempoRange low`);
367
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, high, low, 1e3, `${label} tempoRange high`);
486
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, low, Number.MIN_VALUE, 1e3, `${label} tempoRange low`);
487
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, high, low, 1e3, `${label} tempoRange high`);
368
488
  }
369
489
  return entry;
370
490
  }
@@ -386,72 +506,53 @@ function atLevel(table, difficulty) {
386
506
  return low + (high - low) * (level - lower);
387
507
  }
388
508
  function assertDifficulty(difficulty, label = "complexity difficulty") {
389
- return _chunkQ2WGKJNNcjs.assertRange.call(void 0, difficulty, MIN_DIFFICULTY, MAX_DIFFICULTY, label);
509
+ return _chunkS5BKZZGMcjs.assertRange.call(void 0, difficulty, MIN_DIFFICULTY, MAX_DIFFICULTY, label);
390
510
  }
391
511
  function sustainsStrokes(stepBeats, bpm, difficulty) {
392
- if (difficulty === void 0 || bpm === void 0 || stepBeats <= 0) {
512
+ if (difficulty === void 0 || bpm === void 0) {
513
+ return true;
514
+ }
515
+ assertDifficulty(difficulty);
516
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, bpm, "bpm");
517
+ if (_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, stepBeats, "stepBeats") <= 0) {
393
518
  return true;
394
519
  }
395
520
  const rate = 1 / (stepBeats * secondsPerBeat(bpm));
396
521
  return rate <= atLevel(STROKES_PER_SECOND, difficulty);
397
522
  }
398
523
  function sustainsShift(semitones, stepBeats, bpm, difficulty) {
399
- if (difficulty === void 0 || bpm === void 0 || stepBeats <= 0) {
524
+ if (difficulty === void 0 || bpm === void 0) {
525
+ return true;
526
+ }
527
+ assertDifficulty(difficulty);
528
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, bpm, "bpm");
529
+ _chunkS5BKZZGMcjs.assertFiniteSemitones.call(void 0, semitones);
530
+ if (_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, stepBeats, "stepBeats") <= 0) {
400
531
  return true;
401
532
  }
402
533
  const seconds = stepBeats * secondsPerBeat(bpm);
403
534
  return Math.abs(semitones) / seconds <= atLevel(FRETS_PER_SECOND, difficulty);
404
535
  }
405
536
 
406
- // src/generate/context/draw.ts
407
- function drawFrom(rng, path) {
408
- const draw = rng.at(...path);
409
- if (!(draw >= 0 && draw < 1)) {
410
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`positional draw must be in [0, 1); received ${draw}`);
411
- }
412
- return draw;
413
- }
414
- function drawsFrom(rng, ...prefix) {
415
- const at2 = (...path) => drawFrom(rng, [...prefix, ...path]);
416
- return {
417
- at: at2,
418
- prob: (p, ...path) => _chunkE3F4AHAHcjs.includeAt.call(void 0, rng, p, ...prefix, ...path),
419
- range: (lo, hi, ...path) => {
420
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, lo, "range lower bound");
421
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, hi, "range upper bound");
422
- if (lo > hi) {
423
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
424
- `range lower bound must not exceed upper bound; received ${lo} > ${hi}`
425
- );
426
- }
427
- return lo + Math.floor(at2(...path) * (hi - lo + 1));
428
- },
429
- float: (lo, hi, ...path) => {
430
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, lo, "float lower bound");
431
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, hi, "float upper bound");
432
- if (lo > hi) {
433
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
434
- `float lower bound must not exceed upper bound; received ${lo} > ${hi}`
435
- );
436
- }
437
- return lo + at2(...path) * (hi - lo);
438
- }
439
- };
440
- }
441
-
442
537
  // src/generate/context/index.ts
443
538
  var DEFAULT_SEED = 0;
444
539
  var VERSION_SEGMENT = "v";
445
540
  function resolveContext(input) {
541
+ if (input !== null && typeof input !== "number" && input !== void 0) {
542
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, input, "ctx");
543
+ }
544
+ if (input === null) {
545
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("ctx must be a seed or a generation context; received null");
546
+ }
446
547
  const ctx = typeof input === "number" ? { seed: input } : _nullishCoalesce(input, () => ( { seed: DEFAULT_SEED }));
447
- const seed = _chunkQ2WGKJNNcjs.assertInteger.call(void 0, _nullishCoalesce(ctx.seed, () => ( DEFAULT_SEED)), "seed", 0, 4294967295);
548
+ const seed = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(ctx.seed, () => ( DEFAULT_SEED)), "seed", 0, 4294967295);
448
549
  const complexity = _nullishCoalesce(ctx.complexity, () => ( {}));
449
- const rhythmic = complexity.rhythmic === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertRange.call(void 0, complexity.rhythmic, 0, 1, "complexity rhythmic");
450
- const harmonic = complexity.harmonic === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertRange.call(void 0, complexity.harmonic, 0, 1, "complexity harmonic");
451
- const ornament2 = complexity.ornament === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertRange.call(void 0, complexity.ornament, 0, 1, "complexity ornament");
550
+ const rhythmic = complexity.rhythmic === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertRange.call(void 0, complexity.rhythmic, 0, 1, "complexity rhythmic");
551
+ const harmonic = complexity.harmonic === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertRange.call(void 0, complexity.harmonic, 0, 1, "complexity harmonic");
552
+ const ornament2 = complexity.ornament === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertRange.call(void 0, complexity.ornament, 0, 1, "complexity ornament");
452
553
  const difficulty = complexity.difficulty === void 0 ? void 0 : assertDifficulty(complexity.difficulty);
453
- const bpm = ctx.bpm === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertRange.call(void 0, ctx.bpm, Number.MIN_VALUE, 1e3, "bpm");
454
- const algorithmVersion = _chunkE3F4AHAHcjs.resolveAlgorithmVersion.call(void 0, ctx.algorithmVersion);
554
+ const bpm = ctx.bpm === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertRange.call(void 0, ctx.bpm, Number.MIN_VALUE, 1e3, "bpm");
555
+ const algorithmVersion = _chunkCGBADIO5cjs.resolveAlgorithmVersion.call(void 0, ctx.algorithmVersion);
455
556
  const supplied = ctx.rng;
456
557
  const instruments = ctx.instruments;
457
558
  const vocabulary = (_nullishCoalesce(ctx.vocabulary, () => ( []))).map(
@@ -465,70 +566,109 @@ function resolveContext(input) {
465
566
  difficulty,
466
567
  bpm,
467
568
  algorithmVersion,
468
- instrument: (part) => _optionalChain([instruments, 'optionalAccess', _10 => _10[part]]),
569
+ instrument: (part) => _optionalChain([instruments, 'optionalAccess', _4 => _4[part]]),
469
570
  vocabulary,
470
- part: (name) => supplied ? drawsFrom(supplied, name, VERSION_SEGMENT, algorithmVersion) : drawsFrom(_chunkE3F4AHAHcjs.createPositionalRng.call(void 0, _chunkE3F4AHAHcjs.deriveSeed.call(void 0, seed, name, VERSION_SEGMENT, algorithmVersion)))
571
+ part: (name) => supplied ? drawsFrom(supplied, name, VERSION_SEGMENT, algorithmVersion) : drawsFrom(_chunkCGBADIO5cjs.createPositionalRng.call(void 0, _chunkCGBADIO5cjs.deriveSeed.call(void 0, seed, name, VERSION_SEGMENT, algorithmVersion)))
471
572
  };
472
573
  }
473
574
  function resolveContextWith(input, sugar) {
474
575
  const base = resolveContext(input);
475
- const ornament2 = _nullishCoalesce(base.ornament, () => ( (sugar.ornament === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertRange.call(void 0, sugar.ornament, 0, 1, "complexity ornament"))));
576
+ const supplied = sugar.ornament === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertRange.call(void 0, sugar.ornament, 0, 1, "complexity ornament");
577
+ const ornament2 = _nullishCoalesce(base.ornament, () => ( supplied));
476
578
  return { ...base, ornament: ornament2 };
477
579
  }
478
580
 
479
581
  // src/generate/bass/internal.ts
480
582
  var STRONG_VELOCITY = 100;
481
583
  var WEAK_VELOCITY = 80;
482
- var EPS = 1e-9;
584
+ var DEFAULT_TS = { numerator: 4, denominator: 4 };
585
+ var DEFAULT_OCTAVE = 2;
483
586
  function placePc(pc, anchor, low) {
484
- const high = low + 12;
485
- let midi = pc + 12 * Math.round((anchor - pc) / 12);
486
- while (midi < low) {
487
- midi += 12;
587
+ return foldIntoBand(pc + 12 * Math.round((anchor - pc) / 12), low);
588
+ }
589
+ function placeAboveBass(pc, bassMidi) {
590
+ return foldIntoBand(pc + 12 * Math.round((bassMidi - pc) / 12), bassMidi);
591
+ }
592
+ function placeRoot(pc, low) {
593
+ return low + _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pc - low);
594
+ }
595
+ function barTiles(startBeat, endBeat, ts) {
596
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
597
+ const tiles = [];
598
+ for (let index = 0; ; index += 1) {
599
+ const tileStart = startBeat + index * barBeats;
600
+ if (tileStart >= endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
601
+ break;
602
+ }
603
+ tiles.push({ startBeat: tileStart, endBeat: Math.min(tileStart + barBeats, endBeat) });
488
604
  }
489
- while (midi > high) {
490
- midi -= 12;
605
+ return tiles;
606
+ }
607
+ function midBarPulse(barStart, ts) {
608
+ const pulse2 = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
609
+ return barStart + Math.floor((_chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts) / 2 + _chunkS5BKZZGMcjs.BEAT_EPS) / pulse2) * pulse2;
610
+ }
611
+ function assertBassSegments(segments) {
612
+ for (let index = 0; index < segments.length; index += 1) {
613
+ const segment = segments[index];
614
+ if (segment === void 0) {
615
+ continue;
616
+ }
617
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, segment.startBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].startBeat`);
618
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, segment.endBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].endBeat`);
619
+ if (segment.endBeat <= segment.startBeat) {
620
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`segments[${index}] must have a positive duration`);
621
+ }
622
+ const previous = segments[index - 1];
623
+ if (previous !== void 0 && segment.startBeat < previous.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
624
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
625
+ `bass segments must not overlap: segments ${index - 1} and ${index} overlap`
626
+ );
627
+ }
491
628
  }
492
- return midi;
493
629
  }
494
630
  function chordTonePcs(chord) {
495
631
  const seen = /* @__PURE__ */ new Set();
496
632
  const pcs = [];
497
633
  for (const interval of chord.intervals) {
498
- const pc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
634
+ const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
499
635
  if (!seen.has(pc)) {
500
636
  seen.add(pc);
501
637
  pcs.push(pc);
502
638
  }
503
639
  }
504
640
  if (pcs.length === 0) {
505
- pcs.push(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc));
641
+ pcs.push(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc));
506
642
  }
507
643
  return pcs;
508
644
  }
509
- function fifthPcOf(chord) {
510
- const fifths = new Set(chord.intervals.map((i) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, i)));
511
- const chosen = fifths.has(7) ? 7 : fifths.has(6) ? 6 : fifths.has(8) ? 8 : void 0;
512
- return chosen === void 0 ? _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc) : _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + chosen);
645
+ function fifthPcOf2(chord) {
646
+ return _nullishCoalesce(_chunk3R3QVKY5cjs.fifthPcOf.call(void 0, chord), () => ( _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc)));
513
647
  }
514
648
  function bassPcOf(chord) {
515
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
649
+ return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
650
+ }
651
+ function bassToneCycle(chord) {
652
+ const bass = bassPcOf(chord);
653
+ const tones = chordTonePcs(chord);
654
+ const at2 = tones.indexOf(bass);
655
+ return at2 < 0 ? [bass, ...tones] : [...tones.slice(at2), ...tones.slice(0, at2)];
516
656
  }
517
657
  function beatPositions(start, end, ts) {
518
658
  const positions = [];
519
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
520
- const pulse2 = _chunkK4ZJUGF4cjs.pulseBeats.call(void 0, ts);
659
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
660
+ const pulse2 = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
521
661
  const firstBar = Math.floor(start / barBeats);
522
- const lastBar = Math.floor((end - EPS) / barBeats);
662
+ const lastBar = Math.floor((end - _chunkS5BKZZGMcjs.BEAT_EPS) / barBeats);
523
663
  for (let bar2 = firstBar; bar2 <= lastBar; bar2 += 1) {
524
664
  const barStart = bar2 * barBeats;
525
- const firstPulse = Math.max(0, Math.ceil((start - barStart - EPS) / pulse2));
665
+ const firstPulse = Math.max(0, Math.ceil((start - barStart - _chunkS5BKZZGMcjs.BEAT_EPS) / pulse2));
526
666
  for (let index = firstPulse; ; index += 1) {
527
667
  const position = barStart + index * pulse2;
528
- if (position >= barStart + barBeats - EPS || position >= end - EPS) {
668
+ if (position >= barStart + barBeats - _chunkS5BKZZGMcjs.BEAT_EPS || position >= end - _chunkS5BKZZGMcjs.BEAT_EPS) {
529
669
  break;
530
670
  }
531
- if (position >= start - EPS) {
671
+ if (position >= start - _chunkS5BKZZGMcjs.BEAT_EPS) {
532
672
  positions.push(position);
533
673
  }
534
674
  }
@@ -539,70 +679,88 @@ function beatPositions(start, end, ts) {
539
679
  return positions;
540
680
  }
541
681
  function foldIntoBand(midi, low) {
542
- let result = midi;
543
- while (result < low) {
544
- result += 12;
545
- }
546
- while (result > low + 12) {
547
- result -= 12;
548
- }
549
- return result;
682
+ const raised = midi + 12 * Math.max(0, Math.ceil((low - midi) / 12));
683
+ return raised - 12 * Math.max(0, Math.ceil((raised - low - 12) / 12));
550
684
  }
551
685
  function bandFloor(low, instrument) {
552
686
  if (!instrument) {
553
687
  return low;
554
688
  }
555
- const range = _chunkE3F4AHAHcjs.instrumentRange.call(void 0, instrument);
556
- let floor = low;
557
- while (floor + 12 > range.high) {
558
- floor -= 12;
559
- }
560
- while (floor < range.low) {
561
- floor += 12;
562
- }
563
- return floor;
689
+ const range = _chunkCGBADIO5cjs.instrumentRange.call(void 0, instrument);
690
+ const dropped = low - 12 * Math.max(0, Math.ceil((low + 12 - range.high) / 12));
691
+ return dropped + 12 * Math.max(0, Math.ceil((range.low - dropped) / 12));
564
692
  }
565
- function approachNote(target, from, low, key, chromatic) {
693
+ function approachNote(target, from, key, chromatic) {
566
694
  const dir = from <= target ? -1 : 1;
695
+ const near = neighborOnSide(target, dir, key, chromatic);
696
+ const far = neighborOnSide(target, -dir, key, chromatic);
697
+ const playable = (midi) => midi >= 0 && midi <= 127;
698
+ if (playable(near) && near !== from) {
699
+ return near;
700
+ }
701
+ return playable(far) ? far : near;
702
+ }
703
+ function neighborOnSide(target, dir, key, chromatic) {
567
704
  const semitone = target + dir;
568
705
  if (chromatic) {
569
- return foldIntoBand(semitone, low);
706
+ return semitone;
570
707
  }
571
- const cand = _chunkUADYAYATcjs.nearestScaleTone.call(void 0, target + dir * 2, key);
708
+ const cand = _chunk3R3QVKY5cjs.nearestScaleTone.call(void 0, target + dir * 2, key);
572
709
  const step = Math.abs(cand - target);
573
- return foldIntoBand(step >= 1 && step <= 2 ? cand : semitone, low);
710
+ return step >= 1 && step <= 2 ? cand : semitone;
711
+ }
712
+ function bassRegister(named, given, octave) {
713
+ const instrument = given === void 0 ? named !== void 0 && named.kind === "stringed" ? named : void 0 : _chunkCGBADIO5cjs.toStringedProfile.call(void 0, given, "instrument");
714
+ return { instrument, low: bandFloor(octave * 12 + 12, instrument) };
574
715
  }
575
716
 
576
717
  // src/generate/vocabulary/transform.ts
577
718
  var BAR_STEPS = 16;
578
719
  var BEAT_STEPS = 4;
579
720
  var STEP_BEATS = 1 / BEAT_STEPS;
721
+ function assertGridEvents(events) {
722
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, events, "figure events");
723
+ events.forEach((event, index) => {
724
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, event, `figure events[${index}]`);
725
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, event.step, `figure events[${index}].step`);
726
+ });
727
+ return events;
728
+ }
729
+ function assertSpanSteps(spanSteps) {
730
+ return _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, spanSteps, "figure span steps");
731
+ }
580
732
  function streamOf(event) {
581
733
  return _nullishCoalesce(_nullishCoalesce(event.limb, () => ( event.voice)), () => ( ""));
582
734
  }
583
- function gridMetricWeight(step) {
584
- const inBar = (step % BAR_STEPS + BAR_STEPS) % BAR_STEPS;
585
- if (inBar === 0) {
586
- return 4;
587
- }
588
- if (inBar % 8 === 0) {
589
- return 3;
590
- }
591
- if (inBar % 4 === 0) {
592
- return 2;
593
- }
594
- if (inBar % 2 === 0) {
595
- return 1;
596
- }
597
- return 0;
598
- }
599
- function thin(events, amount) {
600
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, amount, 0, 1, "thin amount");
735
+ var FIGURE_TS = { numerator: 4, denominator: 4 };
736
+ function subdivisionBeats(ts) {
737
+ const pulse2 = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
738
+ return _chunkS5BKZZGMcjs.isCompound.call(void 0, ts) ? pulse2 / 3 : pulse2 / 2;
739
+ }
740
+ function gridMetricWeight(step, ts) {
741
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, step, "step");
742
+ return stepWeight(step, _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts")));
743
+ }
744
+ function stepWeight(step, ts) {
745
+ const beats = step * STEP_BEATS;
746
+ const weight = _chunkS5BKZZGMcjs.metricWeight.call(void 0, beats, ts);
747
+ if (weight > 0) {
748
+ return weight + 1;
749
+ }
750
+ const subdivision = subdivisionBeats(ts);
751
+ const divisions = beats / subdivision;
752
+ return Math.abs(divisions - Math.round(divisions)) < 1e-9 ? 1 : 0;
753
+ }
754
+ function thin(events, amount, ts) {
755
+ const figure = assertGridEvents(events);
756
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, amount, 0, 1, "thin amount");
757
+ const signature = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
601
758
  const floor = Math.min(4, Math.floor(amount * 5));
602
- return events.filter((event) => gridMetricWeight(event.step) >= floor);
759
+ return figure.filter((event) => stepWeight(event.step, signature) >= floor);
603
760
  }
604
761
  function double(events, spanSteps = BAR_STEPS) {
605
- const sorted = [...events].sort((a, b) => a.step - b.step);
762
+ assertSpanSteps(spanSteps);
763
+ const sorted = [...assertGridEvents(events)].sort((a, b) => a.step - b.step);
606
764
  const out = [];
607
765
  for (let i = 0; i < sorted.length; i += 1) {
608
766
  const event = sorted[i];
@@ -610,7 +768,7 @@ function double(events, spanSteps = BAR_STEPS) {
610
768
  continue;
611
769
  }
612
770
  out.push(event);
613
- const next = _nullishCoalesce(_optionalChain([sorted, 'access', _11 => _11[i + 1], 'optionalAccess', _12 => _12.step]), () => ( spanSteps));
771
+ const next = _nullishCoalesce(_optionalChain([sorted, 'access', _5 => _5[i + 1], 'optionalAccess', _6 => _6.step]), () => ( spanSteps));
614
772
  const mid = event.step + (next - event.step) / 2;
615
773
  if (Number.isInteger(mid) && mid > event.step && mid < spanSteps) {
616
774
  out.push({ ...event, step: mid, velocity: event.velocity * ADDED_NOTE_VELOCITY });
@@ -620,30 +778,46 @@ function double(events, spanSteps = BAR_STEPS) {
620
778
  }
621
779
  var ADDED_NOTE_VELOCITY = 0.75;
622
780
  function halfTime(events, spanSteps = BAR_STEPS) {
623
- return events.map((event) => ({ ...event, step: event.step * 2 })).filter((event) => event.step < spanSteps);
781
+ assertSpanSteps(spanSteps);
782
+ return assertGridEvents(events).map((event) => ({ ...event, step: event.step * 2 })).filter((event) => event.step < spanSteps);
624
783
  }
625
784
  function doubleTime(events, spanSteps = BAR_STEPS) {
626
- const out = [];
785
+ assertSpanSteps(spanSteps);
786
+ assertGridEvents(events);
787
+ const merged = /* @__PURE__ */ new Map();
788
+ const place = (event, step, velocity) => {
789
+ const key = `${streamOf(event)}@${step}`;
790
+ const held = merged.get(key);
791
+ if (held === void 0 || velocity > held.velocity) {
792
+ merged.set(key, { ...event, step, velocity });
793
+ }
794
+ };
627
795
  for (const event of events) {
628
796
  const step = Math.round(event.step / 2);
629
- out.push({ ...event, step });
797
+ place(event, step, event.velocity);
630
798
  const repeated = step + spanSteps / 2;
631
799
  if (repeated < spanSteps) {
632
- out.push({ ...event, step: repeated, velocity: event.velocity * ADDED_NOTE_VELOCITY });
800
+ place(event, repeated, event.velocity * ADDED_NOTE_VELOCITY);
633
801
  }
634
802
  }
635
- return out.sort((a, b) => a.step - b.step);
803
+ return [...merged.values()].sort((a, b) => a.step - b.step);
636
804
  }
637
805
  function syncopate(events, amount, draw, ...path) {
638
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, amount, 0, 1, "syncopate amount");
639
- const occupied = new Set(events.map((event) => `${streamOf(event)}@${event.step}`));
640
- const out = [...events];
641
- for (const event of events) {
806
+ const figure = assertGridEvents(events);
807
+ const sampler = assertDraw(draw);
808
+ assertSeedPath(path);
809
+ const asked = typeof amount === "number" ? void 0 : _chunkS5BKZZGMcjs.assertRecord.call(void 0, amount, "amount");
810
+ const dial = asked === void 0 ? amount : asked.amount;
811
+ const signature = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(_optionalChain([asked, 'optionalAccess', _7 => _7.ts]), () => ( FIGURE_TS)), "ts"));
812
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, dial, 0, 1, "syncopate amount");
813
+ const occupied = new Set(figure.map((event) => `${streamOf(event)}@${event.step}`));
814
+ const out = [...figure];
815
+ for (const event of figure) {
642
816
  const anticipated = event.step - 1;
643
- if (gridMetricWeight(event.step) < 2 || anticipated < 1 || occupied.has(`${streamOf(event)}@${anticipated}`)) {
817
+ if (stepWeight(event.step, signature) < 2 || anticipated < 1 || occupied.has(`${streamOf(event)}@${anticipated}`)) {
644
818
  continue;
645
819
  }
646
- if (!draw.prob(amount, ...path, "syncopate", event.step)) {
820
+ if (!sampler.prob(dial, ...path, "syncopate", event.step)) {
647
821
  continue;
648
822
  }
649
823
  out.push({ ...event, step: anticipated, velocity: event.velocity * ADDED_NOTE_VELOCITY });
@@ -651,35 +825,55 @@ function syncopate(events, amount, draw, ...path) {
651
825
  return out.sort((a, b) => a.step - b.step);
652
826
  }
653
827
  function ornamentBy(events, isOrnament, amount, draw, ...path) {
654
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, amount, 0, 1, "ornament amount");
655
- return events.filter(
656
- (event) => !isOrnament(event) || draw.prob(amount, ...path, "ornament", event.step)
828
+ const figure = assertGridEvents(events);
829
+ const marks = _chunkS5BKZZGMcjs.assertFunction.call(void 0, isOrnament, "isOrnament");
830
+ const sampler = assertDraw(draw);
831
+ assertSeedPath(path);
832
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, amount, 0, 1, "ornament amount");
833
+ return figure.filter(
834
+ (event) => !marks(event) || sampler.prob(amount, ...path, "ornament", event.step)
657
835
  );
658
836
  }
659
837
  var NEUTRAL_RHYTHMIC = 0.5;
660
838
  function deform(events, opts, draw, ...path) {
661
- const spanSteps = _nullishCoalesce(opts.spanSteps, () => ( BAR_STEPS));
662
- let out = opts.rate === "half" ? halfTime(events, spanSteps) : opts.rate === "double" ? doubleTime(events, spanSteps) : [...events];
663
- if (opts.rhythmic !== void 0) {
664
- const rhythmic = _chunkQ2WGKJNNcjs.assertRange.call(void 0, opts.rhythmic, 0, 1, "complexity rhythmic");
839
+ const figure = assertGridEvents(events);
840
+ const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
841
+ assertDraw(draw);
842
+ assertSeedPath(path);
843
+ const spanSteps = assertSpanSteps(asked.spanSteps === void 0 ? BAR_STEPS : asked.spanSteps);
844
+ const ts = _nullishCoalesce(asked.ts, () => ( FIGURE_TS));
845
+ let out = asked.rate === "half" ? halfTime(figure, spanSteps) : asked.rate === "double" ? doubleTime(figure, spanSteps) : [...figure];
846
+ if (asked.rhythmic !== void 0) {
847
+ const rhythmic = _chunkS5BKZZGMcjs.assertRange.call(void 0, asked.rhythmic, 0, 1, "complexity rhythmic");
665
848
  if (rhythmic < NEUTRAL_RHYTHMIC) {
666
- out = thin(out, (NEUTRAL_RHYTHMIC - rhythmic) / NEUTRAL_RHYTHMIC);
849
+ out = thin(out, (NEUTRAL_RHYTHMIC - rhythmic) / NEUTRAL_RHYTHMIC, ts);
667
850
  } else if (rhythmic > NEUTRAL_RHYTHMIC) {
668
- out = syncopate(out, (rhythmic - NEUTRAL_RHYTHMIC) / NEUTRAL_RHYTHMIC, draw, ...path);
851
+ out = syncopate(
852
+ out,
853
+ { amount: (rhythmic - NEUTRAL_RHYTHMIC) / NEUTRAL_RHYTHMIC, ts },
854
+ draw,
855
+ ...path
856
+ );
669
857
  }
670
858
  }
671
- if (opts.ornament !== void 0 && opts.isOrnament) {
672
- const isOrnament = opts.isOrnament;
673
- out = ornamentBy(out, (event) => isOrnament(event), opts.ornament, draw, ...path);
859
+ if (asked.ornament !== void 0 && asked.isOrnament) {
860
+ const isOrnament = _chunkS5BKZZGMcjs.assertFunction.call(void 0, asked.isOrnament, "opts.isOrnament");
861
+ out = ornamentBy(out, (event) => isOrnament(event), asked.ornament, draw, ...path);
674
862
  }
675
863
  return out.sort((a, b) => a.step - b.step);
676
864
  }
677
865
  function withinCeiling(events, bpm, difficulty) {
678
- if (difficulty === void 0 || bpm === void 0 || events.length < 2) {
866
+ const figure = assertGridEvents(events);
867
+ if (difficulty === void 0 || bpm === void 0) {
868
+ return true;
869
+ }
870
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, bpm, "bpm");
871
+ assertDifficulty(difficulty);
872
+ if (figure.length < 2) {
679
873
  return true;
680
874
  }
681
875
  const byStream = /* @__PURE__ */ new Map();
682
- for (const event of events) {
876
+ for (const event of figure) {
683
877
  const stream = streamOf(event);
684
878
  const steps = byStream.get(stream);
685
879
  if (steps) {
@@ -704,7 +898,79 @@ function withinCeiling(events, bpm, difficulty) {
704
898
  return sustainsStrokes(closest * STEP_BEATS, bpm, difficulty);
705
899
  }
706
900
 
707
- // src/generate/bass/licks.ts
901
+ // src/generate/bass/degrees.ts
902
+ var MAJOR_DEGREE_SEMITONES = [0, 2, 4, 5, 7, 9, 11];
903
+ var HEPTATONIC_SIZE = 7;
904
+ function impliedScaleTones(chord) {
905
+ if (chordDegreeSemitone(7, chord) === void 0) {
906
+ return void 0;
907
+ }
908
+ for (const match of _chunkPM4XJE2Ycjs.chordScales.call(void 0, chord)) {
909
+ const mask = _chunk3R3QVKY5cjs.namedScaleMask.call(void 0, match.name);
910
+ if (mask === void 0) {
911
+ continue;
912
+ }
913
+ const tones = [];
914
+ for (let offset = 0; offset < 12; offset += 1) {
915
+ if ((mask >> offset & 1) === 1) {
916
+ tones.push(offset);
917
+ }
918
+ }
919
+ if (tones.length === HEPTATONIC_SIZE) {
920
+ return tones;
921
+ }
922
+ }
923
+ return void 0;
924
+ }
925
+ function replacedDegreeSemitone(degree, chord) {
926
+ if (degree !== 3) {
927
+ return void 0;
928
+ }
929
+ const sounds = (semitones) => chord.intervals.some((interval) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === semitones);
930
+ if (sounds(3) || sounds(4)) {
931
+ return void 0;
932
+ }
933
+ return sounds(5) ? 5 : sounds(2) ? 2 : void 0;
934
+ }
935
+ function degreeSemitone(degree, alter, chord, key, implied) {
936
+ const octaves = Math.floor((degree - 1) / 7);
937
+ const within = (degree - 1) % 7 + 1;
938
+ const template = _nullishCoalesce(MAJOR_DEGREE_SEMITONES[within - 1], () => ( 0));
939
+ const semitone = alter !== 0 ? template : _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(chordDegreeSemitone(within, chord), () => ( replacedDegreeSemitone(within, chord))), () => ( _optionalChain([implied, 'optionalAccess', _8 => _8[within - 1]]))), () => ( // A degree neither the chord nor its quality settles is a passing tone,
940
+ // so the key decides it.
941
+ _chunk3R3QVKY5cjs.nearestScaleTone.call(void 0, chord.rootPc + template, key) - chord.rootPc));
942
+ return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, semitone) + 12 * octaves + alter;
943
+ }
944
+ function chordDegreeSemitone(degree, chord) {
945
+ for (const interval of chord.intervals) {
946
+ if (_chunk3R3QVKY5cjs.degreeOfInterval.call(void 0, interval, chord) === degree) {
947
+ return interval;
948
+ }
949
+ }
950
+ return void 0;
951
+ }
952
+
953
+ // src/generate/vocabulary/freeze.ts
954
+ function freezeReachable(value, seen) {
955
+ if (value === null || typeof value !== "object") {
956
+ return;
957
+ }
958
+ const object = value;
959
+ if (seen.has(object)) {
960
+ return;
961
+ }
962
+ seen.add(object);
963
+ Object.freeze(object);
964
+ for (const child of Object.values(object)) {
965
+ freezeReachable(child, seen);
966
+ }
967
+ }
968
+ function deepFreeze(value) {
969
+ freezeReachable(value, /* @__PURE__ */ new WeakSet());
970
+ return value;
971
+ }
972
+
973
+ // src/generate/bass/licks-data.ts
708
974
  function isLickMaterial(material) {
709
975
  const candidate = material;
710
976
  return typeof candidate === "object" && candidate !== null && typeof candidate.lengthSteps === "number" && Array.isArray(candidate.notes);
@@ -714,7 +980,7 @@ function note(degree, step, velocity, extra) {
714
980
  }
715
981
  var BAR = 4 * BEAT_STEPS;
716
982
  var FOUR_FOUR = { numerator: 4, denominator: 4 };
717
- var BASS_LICKS = Object.freeze([
983
+ var BASS_LICKS = deepFreeze([
718
984
  {
719
985
  id: "motownWalkDown",
720
986
  genre: "motown",
@@ -759,7 +1025,7 @@ var BASS_LICKS = Object.freeze([
759
1025
  id: "funkSixteenthPop",
760
1026
  genre: "funk",
761
1027
  difficulty: 4,
762
- articulations: ["mute", "slide"],
1028
+ articulations: ["mute"],
763
1029
  ts: FOUR_FOUR,
764
1030
  tempoRange: [84, 124],
765
1031
  material: {
@@ -842,7 +1108,7 @@ var BASS_LICKS = Object.freeze([
842
1108
  id: "gospelPassing",
843
1109
  genre: "gospel",
844
1110
  difficulty: 4,
845
- articulations: ["slide"],
1111
+ articulations: [],
846
1112
  ts: FOUR_FOUR,
847
1113
  tempoRange: [60, 108],
848
1114
  material: {
@@ -883,7 +1149,7 @@ var BASS_LICKS = Object.freeze([
883
1149
  id: "reggaeOffbeatDrop",
884
1150
  genre: "reggae",
885
1151
  difficulty: 3,
886
- articulations: ["mute"],
1152
+ articulations: [],
887
1153
  ts: FOUR_FOUR,
888
1154
  tempoRange: [64, 104],
889
1155
  material: {
@@ -902,103 +1168,61 @@ var BASS_LICKS = Object.freeze([
902
1168
  }
903
1169
  }
904
1170
  ]);
905
- var MAJOR_DEGREE_SEMITONES = [0, 2, 4, 5, 7, 9, 11];
906
- function degreeSemitone(degree, alter, chord, key) {
907
- const octaves = Math.floor((degree - 1) / 7);
908
- const within = (degree - 1) % 7 + 1;
909
- const fromChord = chordDegreeSemitone(within, chord);
910
- const template = _nullishCoalesce(MAJOR_DEGREE_SEMITONES[within - 1], () => ( 0));
911
- const semitone = _nullishCoalesce(fromChord, () => ( // A degree the chord does not name is a passing tone, so the key decides it.
912
- _chunkUADYAYATcjs.nearestScaleTone.call(void 0, chord.rootPc + template, key) - chord.rootPc));
913
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, semitone) + 12 * octaves + alter;
914
- }
915
- function chordDegreeSemitone(degree, chord) {
916
- for (const interval of chord.intervals) {
917
- if (degreeOfInterval(interval) === degree) {
918
- return interval;
919
- }
920
- }
921
- return void 0;
922
- }
923
- function degreeOfInterval(interval) {
924
- const within = (interval % 12 + 12) % 12;
925
- if (within === 0) {
926
- return 1;
927
- }
928
- if (within === 1 || within === 2) {
929
- return 2;
930
- }
931
- if (within === 3 || within === 4) {
932
- return 3;
933
- }
934
- if (within === 5) {
935
- return 4;
936
- }
937
- if (within === 6 || within === 7 || within === 8) {
938
- return 5;
939
- }
940
- if (within === 9) {
941
- return 6;
942
- }
943
- return 7;
944
- }
945
- var DEFAULT_TS = FOUR_FOUR;
946
- var DEFAULT_OCTAVE = 2;
1171
+
1172
+ // src/generate/bass/licks.ts
947
1173
  var DEFAULT_BPM = 120;
948
1174
  var DEFAULT_LICK_DENSITY = 0.6;
949
- var EPS2 = 1e-9;
950
1175
  function placeLicks(timeline, key, opts) {
951
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, timeline.length, "lick segments", opts.budget);
952
- const genre = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.genre, GENRES, "lick genre");
953
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS));
954
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts, "lick time signature");
955
- const octave = _nullishCoalesce(opts.octave, () => ( DEFAULT_OCTAVE));
956
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, octave, "lick octave", -1, 8);
957
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
958
- const segments = timeline.map((segment) => ({ ...segment, chord: _chunkUADYAYATcjs.toChordData.call(void 0, segment.chord) })).sort((a, b) => a.startBeat - b.startBeat);
959
- for (const segment of segments) {
960
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, segment.startBeat, 0, Number.MAX_SAFE_INTEGER, "lick segment startBeat");
961
- if (segment.endBeat <= segment.startBeat) {
962
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
963
- `lick segment at beat ${segment.startBeat} must have a positive duration`
964
- );
965
- }
966
- }
1176
+ const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
1177
+ const placement = _chunkS5BKZZGMcjs.assertArray.call(void 0,
1178
+ timeline,
1179
+ "timeline"
1180
+ );
1181
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, placement.length, "lick segments", asked.budget);
1182
+ const genre = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, asked.genre, GENRES, "lick genre");
1183
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(asked.ts, () => ( DEFAULT_TS)), "ts"));
1184
+ const octave = _nullishCoalesce(asked.octave, () => ( DEFAULT_OCTAVE));
1185
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, octave, "lick octave", -1, 8);
1186
+ const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
1187
+ const segments = placement.map((segment) => ({ ...segment, chord: _chunk3R3QVKY5cjs.toChordData.call(void 0, segment.chord) })).sort((a, b) => a.startBeat - b.startBeat);
1188
+ assertBassSegments(segments);
967
1189
  if (segments.length === 0) {
968
1190
  return [];
969
1191
  }
970
- const resolved = resolveContext(opts.ctx);
1192
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
1193
+ const estimatedBars = segments.reduce(
1194
+ (count, segment) => count + Math.max(1, Math.ceil((segment.endBeat - segment.startBeat) / barBeats)),
1195
+ 0
1196
+ );
1197
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, estimatedBars, "lick bars", asked.budget);
1198
+ const resolved = resolveContext(asked.ctx);
971
1199
  const draw = resolved.part("bass");
972
1200
  const bpm = _nullishCoalesce(resolved.bpm, () => ( DEFAULT_BPM));
973
1201
  const density = _nullishCoalesce(resolved.rhythmic, () => ( DEFAULT_LICK_DENSITY));
974
- const difficulty = _nullishCoalesce(resolved.difficulty, () => ( (opts.difficulty === void 0 ? void 0 : assertDifficulty(opts.difficulty, "lick difficulty"))));
975
- const named = resolved.instrument("bass");
976
- const instrument = _nullishCoalesce(opts.instrument, () => ( (named !== void 0 && named.kind === "stringed" ? named : void 0)));
977
- const low = bandFloor(octave * 12 + 12, instrument);
1202
+ const supplied = asked.difficulty === void 0 ? void 0 : assertDifficulty(asked.difficulty, "lick difficulty");
1203
+ const difficulty = _nullishCoalesce(resolved.difficulty, () => ( supplied));
1204
+ const { instrument, low } = bassRegister(resolved.instrument("bass"), asked.instrument, octave);
978
1205
  const dictionary = mergeVocabulary(
979
1206
  BASS_LICKS,
980
1207
  vocabularyOfKind(resolved.vocabulary, isLickMaterial)
981
1208
  );
982
1209
  const playable = instrument === void 0 ? {} : { articulations: instrument.articulations };
983
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
984
1210
  const raw = [];
985
- let anchor = low;
986
- let rootAnchor = low;
987
1211
  for (let index = 0; index < segments.length; index += 1) {
988
1212
  const segment = segments[index];
989
1213
  if (!segment) {
990
1214
  continue;
991
1215
  }
992
1216
  const rootPc = bassPcOf(segment.chord);
993
- const onsets = [];
1217
+ const rootMidi = placeRoot(rootPc, low);
1218
+ const implied = impliedScaleTones(segment.chord);
994
1219
  let lastTilePlayed = false;
1220
+ let playedTile;
995
1221
  let firstOnset = segment.startBeat;
996
- for (let tile = 0; ; tile += 1) {
997
- const tileStart = segment.startBeat + tile * barBeats;
1222
+ const tiles = barTiles(segment.startBeat, segment.endBeat, ts);
1223
+ for (let tile = 0; tile < tiles.length; tile += 1) {
1224
+ const tileStart = _nullishCoalesce(_optionalChain([tiles, 'access', _9 => _9[tile], 'optionalAccess', _10 => _10.startBeat]), () => ( segment.startBeat));
998
1225
  const remaining = segment.endBeat - tileStart;
999
- if (remaining <= EPS2) {
1000
- break;
1001
- }
1002
1226
  const entry = pickVocabulary(
1003
1227
  dictionary,
1004
1228
  { genre, bpm, ts, difficulty, quality: segment.chord.quality, ...playable },
@@ -1016,30 +1240,36 @@ function placeLicks(timeline, key, opts) {
1016
1240
  draw,
1017
1241
  [index, tile],
1018
1242
  bpm,
1019
- difficulty
1243
+ difficulty,
1244
+ ts
1020
1245
  ) : void 0;
1021
1246
  lastTilePlayed = notes !== void 0;
1247
+ if (notes !== void 0 && entry !== void 0) {
1248
+ playedTile = { material: entry.material, tileStart };
1249
+ }
1022
1250
  const rootStep = entry === void 0 ? 0 : anchorStepOf(entry.material, remaining);
1023
1251
  const rootAt = tileStart + rootStep * STEP_BEATS;
1024
1252
  if (tile === 0) {
1025
1253
  firstOnset = rootAt;
1026
1254
  }
1027
- onsets.push(rootAt);
1028
- rootAnchor = placePc(rootPc, rootAnchor, low);
1029
- anchor = rootAnchor;
1030
- raw.push({ startBeat: rootAt, pitch: rootAnchor, velocity: STRONG_VELOCITY });
1255
+ raw.push({ startBeat: rootAt, pitch: rootMidi, velocity: STRONG_VELOCITY });
1031
1256
  if (notes) {
1032
- const figureRoot = placePc(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, segment.chord.rootPc), rootAnchor, low);
1257
+ const figureRoot = segment.chord.bassPc === void 0 ? placePc(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, segment.chord.rootPc), rootMidi, low) : placeAboveBass(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, segment.chord.rootPc), rootMidi);
1033
1258
  for (const lickNote of notes) {
1034
1259
  const at2 = tileStart + lickNote.step * STEP_BEATS;
1035
- const offset = degreeSemitone(lickNote.degree, _nullishCoalesce(lickNote.alter, () => ( 0)), segment.chord, scale);
1036
- anchor = figureRoot + offset;
1037
- onsets.push(at2);
1260
+ const offset = degreeSemitone(
1261
+ lickNote.degree,
1262
+ _nullishCoalesce(lickNote.alter, () => ( 0)),
1263
+ segment.chord,
1264
+ scale,
1265
+ implied
1266
+ );
1267
+ const pitch = Math.abs(at2 - rootAt) < _chunkS5BKZZGMcjs.BEAT_EPS && lickNote.degree === 1 && (_nullishCoalesce(lickNote.alter, () => ( 0))) === 0 ? rootMidi : figureRoot + offset;
1038
1268
  raw.push({
1039
1269
  startBeat: at2,
1040
- pitch: anchor,
1270
+ pitch,
1041
1271
  velocity: Math.round(
1042
- (_chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, at2, ts) ? STRONG_VELOCITY : WEAK_VELOCITY) * lickNote.velocity
1272
+ (_chunkS5BKZZGMcjs.isStrongBeat.call(void 0, at2, ts) ? STRONG_VELOCITY : WEAK_VELOCITY) * lickNote.velocity
1043
1273
  ),
1044
1274
  note: lickNote
1045
1275
  });
@@ -1048,19 +1278,21 @@ function placeLicks(timeline, key, opts) {
1048
1278
  }
1049
1279
  const next = segments[index + 1];
1050
1280
  if (next && lastTilePlayed) {
1051
- const approachAt = next.startBeat - STEP_BEATS * BEAT_STEPS;
1052
- const alreadySounds = onsets.some((onset) => Math.abs(onset - approachAt) < EPS2);
1053
- if (approachAt > firstOnset + EPS2 && !alreadySounds) {
1054
- const nextRoot = placePc(bassPcOf(next.chord), rootAnchor, low);
1055
- const midi = approachNote(
1056
- nextRoot,
1057
- anchor,
1058
- low,
1059
- scale,
1060
- draw.prob(0.5, "lickApproach", index)
1061
- );
1062
- anchor = midi;
1063
- raw.push({ startBeat: approachAt, pitch: midi, velocity: WEAK_VELOCITY });
1281
+ const lastBeat = next.startBeat - STEP_BEATS * BEAT_STEPS;
1282
+ const approachAt = _nullishCoalesce(materialOnsetIn(playedTile, lastBeat, next.startBeat), () => ( lastBeat));
1283
+ if (approachAt > firstOnset + _chunkS5BKZZGMcjs.BEAT_EPS) {
1284
+ const nextRoot = placeRoot(bassPcOf(next.chord), low);
1285
+ const occupied = soundingAt(raw, approachAt);
1286
+ const distance = occupied === void 0 ? 0 : Math.abs(occupied.pitch - nextRoot);
1287
+ if (occupied === void 0 || distance < 1 || distance > 2) {
1288
+ const from = lastPitchBefore(raw, approachAt, rootMidi);
1289
+ const midi = approachNote(nextRoot, from, scale, draw.prob(0.5, "lickApproach", index));
1290
+ if (occupied === void 0) {
1291
+ raw.push({ startBeat: approachAt, pitch: midi, velocity: WEAK_VELOCITY });
1292
+ } else {
1293
+ occupied.pitch = midi;
1294
+ }
1295
+ }
1064
1296
  }
1065
1297
  }
1066
1298
  }
@@ -1072,29 +1304,61 @@ function placeLicks(timeline, key, opts) {
1072
1304
  if (!item) {
1073
1305
  continue;
1074
1306
  }
1075
- const nextStart = _nullishCoalesce(_optionalChain([raw, 'access', _13 => _13[i + 1], 'optionalAccess', _14 => _14.startBeat]), () => ( lastEnd));
1076
- const written = _optionalChain([item, 'access', _15 => _15.note, 'optionalAccess', _16 => _16.lengthSteps]);
1307
+ const nextStart = _nullishCoalesce(_optionalChain([raw, 'access', _11 => _11[i + 1], 'optionalAccess', _12 => _12.startBeat]), () => ( lastEnd));
1308
+ const written = _optionalChain([item, 'access', _13 => _13.note, 'optionalAccess', _14 => _14.lengthSteps]);
1077
1309
  const durationBeat = Math.min(
1078
1310
  nextStart - item.startBeat,
1079
1311
  written === void 0 ? Number.POSITIVE_INFINITY : written * STEP_BEATS
1080
1312
  );
1081
- if (durationBeat <= EPS2) {
1313
+ if (durationBeat <= _chunkS5BKZZGMcjs.BEAT_EPS) {
1082
1314
  continue;
1083
1315
  }
1084
- const placed = instrument ? _chunkE3F4AHAHcjs.foldIntoRange.call(void 0, item.pitch, instrument) : item.pitch;
1316
+ const placed = instrument ? _chunkCGBADIO5cjs.foldIntoRange.call(void 0, item.pitch, instrument) : item.pitch;
1085
1317
  const event = {
1086
- pitch: _chunkQ2WGKJNNcjs.clampToMidi.call(void 0, placed, "generated bass pitch"),
1318
+ pitch: _chunkS5BKZZGMcjs.clampToMidi.call(void 0, placed, "generated bass pitch"),
1087
1319
  startBeat: item.startBeat,
1088
1320
  durationBeat,
1089
1321
  velocity: Math.max(1, Math.min(127, item.velocity))
1090
1322
  };
1091
- if (_optionalChain([item, 'access', _17 => _17.note, 'optionalAccess', _18 => _18.articulation])) {
1323
+ if (_optionalChain([item, 'access', _15 => _15.note, 'optionalAccess', _16 => _16.articulation])) {
1092
1324
  event.articulation = item.note.articulation;
1093
1325
  }
1094
1326
  out.push(event);
1095
1327
  }
1096
1328
  return out;
1097
1329
  }
1330
+ function soundingAt(raw, at2) {
1331
+ for (let index = raw.length - 1; index >= 0; index -= 1) {
1332
+ const item = raw[index];
1333
+ if (item !== void 0 && Math.abs(item.startBeat - at2) < _chunkS5BKZZGMcjs.BEAT_EPS) {
1334
+ return item;
1335
+ }
1336
+ }
1337
+ return void 0;
1338
+ }
1339
+ function materialOnsetIn(played, from, to) {
1340
+ if (played === void 0) {
1341
+ return void 0;
1342
+ }
1343
+ let last;
1344
+ for (const note2 of played.material.notes) {
1345
+ const at2 = played.tileStart + note2.step * STEP_BEATS;
1346
+ if (at2 < from - _chunkS5BKZZGMcjs.BEAT_EPS || at2 > to - _chunkS5BKZZGMcjs.BEAT_EPS) {
1347
+ continue;
1348
+ }
1349
+ last = last === void 0 ? at2 : Math.max(last, at2);
1350
+ }
1351
+ return last;
1352
+ }
1353
+ function lastPitchBefore(raw, at2, fallback) {
1354
+ for (let index = raw.length - 1; index >= 0; index -= 1) {
1355
+ const item = raw[index];
1356
+ if (item !== void 0 && item.startBeat < at2 - _chunkS5BKZZGMcjs.BEAT_EPS) {
1357
+ return item.pitch;
1358
+ }
1359
+ }
1360
+ return fallback;
1361
+ }
1098
1362
  function anchorStepOf(material, spanBeats) {
1099
1363
  let first;
1100
1364
  for (const note2 of material.notes) {
@@ -1102,15 +1366,19 @@ function anchorStepOf(material, spanBeats) {
1102
1366
  first = note2.step;
1103
1367
  }
1104
1368
  }
1105
- if (first === void 0 || first < 0 || first * STEP_BEATS >= spanBeats - EPS2) {
1369
+ if (first === void 0 || first < 0 || first * STEP_BEATS >= spanBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
1106
1370
  return 0;
1107
1371
  }
1108
1372
  return first;
1109
1373
  }
1110
- function fitToSegment(material, spanBeats, density, resolved, draw, path, bpm, difficulty) {
1374
+ function fitToSegment(material, spanBeats, density, resolved, draw, path, bpm, difficulty, ts) {
1111
1375
  const deformed = deform(
1112
1376
  material.notes,
1113
1377
  {
1378
+ // The figure is thinned against the bar it is played in, not against a
1379
+ // four-four ladder: a downbeat of the meter in force must outrank an
1380
+ // offbeat of some other meter.
1381
+ ts,
1114
1382
  // The one density the whole generator reads, defaults included: passing
1115
1383
  // the documented default explicitly and leaving it out have to be the
1116
1384
  // same request, and they were not while the figure was deformed by the
@@ -1125,7 +1393,7 @@ function fitToSegment(material, spanBeats, density, resolved, draw, path, bpm, d
1125
1393
  ...path
1126
1394
  );
1127
1395
  const spanSteps = spanBeats / STEP_BEATS;
1128
- const inside = deformed.filter((lickNote) => lickNote.step < spanSteps - EPS2);
1396
+ const inside = deformed.filter((lickNote) => lickNote.step < spanSteps - _chunkS5BKZZGMcjs.BEAT_EPS);
1129
1397
  if (inside.length === 0 || !withinCeiling(inside, bpm, difficulty)) {
1130
1398
  return void 0;
1131
1399
  }
@@ -1140,28 +1408,32 @@ var BASS_STYLES = Object.freeze([
1140
1408
  "walking",
1141
1409
  "arpeggio"
1142
1410
  ]);
1143
- var DEFAULT_TS2 = { numerator: 4, denominator: 4 };
1144
1411
  var DEFAULT_STYLE = "root";
1145
- var DEFAULT_OCTAVE2 = 2;
1146
1412
  var DEFAULT_PICKUP_DENSITY = 0.35;
1413
+ function sounding(ctx, placed) {
1414
+ return ctx.instrument ? _chunkCGBADIO5cjs.foldIntoRange.call(void 0, placed, ctx.instrument) : placed;
1415
+ }
1416
+ function placeTone(ctx, pc) {
1417
+ return ctx.overBass ? placeAboveBass(pc, ctx.rootMidi) : placePc(pc, ctx.rootMidi, ctx.low);
1418
+ }
1147
1419
  function emit(ctx, pos, pc, midiOverride) {
1148
- const placed = _chunkQ2WGKJNNcjs.clampToMidi.call(void 0,
1149
- _nullishCoalesce(midiOverride, () => ( placePc(pc, ctx.prevMidi, ctx.low))),
1150
- "generated bass pitch"
1151
- );
1152
- const midi = ctx.instrument ? _chunkE3F4AHAHcjs.foldIntoRange.call(void 0, placed, ctx.instrument) : placed;
1153
- const velocity = _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, pos, ctx.ts) ? STRONG_VELOCITY : WEAK_VELOCITY;
1420
+ const placed = _chunkS5BKZZGMcjs.clampToMidi.call(void 0, _nullishCoalesce(midiOverride, () => ( placeTone(ctx, pc))), "generated bass pitch");
1421
+ const midi = sounding(ctx, placed);
1422
+ const velocity = _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, pos, ctx.ts) ? STRONG_VELOCITY : WEAK_VELOCITY;
1154
1423
  ctx.notes.push({ startBeat: pos, pitch: midi, velocity });
1424
+ ctx.prevMidi = midi;
1155
1425
  return midi;
1156
1426
  }
1157
1427
  function buildRoot(ctx, seg) {
1158
- ctx.prevMidi = emit(ctx, seg.startBeat, bassPcOf(seg.chord));
1428
+ emit(ctx, seg.startBeat, bassPcOf(seg.chord));
1159
1429
  }
1160
1430
  function buildRootFifth(ctx, seg) {
1161
- ctx.prevMidi = emit(ctx, seg.startBeat, bassPcOf(seg.chord));
1162
- const mid = (seg.startBeat + seg.endBeat) / 2;
1163
- if (mid > seg.startBeat + EPS && mid < seg.endBeat - EPS) {
1164
- ctx.prevMidi = emit(ctx, mid, fifthPcOf(seg.chord));
1431
+ for (const tile of barTiles(seg.startBeat, seg.endBeat, ctx.ts)) {
1432
+ emit(ctx, tile.startBeat, bassPcOf(seg.chord));
1433
+ const answer = midBarPulse(tile.startBeat, ctx.ts);
1434
+ if (answer > tile.startBeat + _chunkS5BKZZGMcjs.BEAT_EPS && answer < tile.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
1435
+ emit(ctx, answer, fifthPcOf2(seg.chord));
1436
+ }
1165
1437
  }
1166
1438
  }
1167
1439
  function buildPop(ctx, seg, index) {
@@ -1169,102 +1441,102 @@ function buildPop(ctx, seg, index) {
1169
1441
  const rootPc = bassPcOf(seg.chord);
1170
1442
  let emitted = false;
1171
1443
  for (const pos of positions) {
1172
- if (_chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, pos, ctx.ts)) {
1173
- ctx.prevMidi = emit(ctx, pos, rootPc);
1444
+ if (_chunkS5BKZZGMcjs.isStrongBeat.call(void 0, pos, ctx.ts)) {
1445
+ emit(ctx, pos, rootPc);
1174
1446
  emitted = true;
1175
1447
  } else if (ctx.draw.prob(ctx.pickupDensity, "pickup", index, pos)) {
1176
- if (ctx.draw.prob(0.5, "pickupKind", index, pos) && reachableLeap(ctx, pos, 12)) {
1177
- const base = placePc(rootPc, ctx.prevMidi, ctx.low);
1178
- const dropped = base - 12;
1179
- emit(ctx, pos, rootPc, dropped >= 0 ? dropped : base);
1180
- } else {
1181
- ctx.prevMidi = emit(ctx, pos, fifthPcOf(seg.chord));
1182
- }
1448
+ emitted = pickup(ctx, seg, pos, index) || emitted;
1183
1449
  }
1184
1450
  }
1185
1451
  if (!emitted) {
1186
- ctx.prevMidi = emit(ctx, seg.startBeat, rootPc);
1452
+ emit(ctx, seg.startBeat, rootPc);
1187
1453
  }
1188
1454
  }
1455
+ function pickup(ctx, seg, pos, index) {
1456
+ const octave = octavePickup(ctx);
1457
+ if (ctx.draw.prob(0.5, "pickupKind", index, pos) && octave !== void 0 && reachableLeap(ctx, pos, Math.abs(sounding(ctx, octave) - ctx.prevMidi))) {
1458
+ emit(ctx, pos, bassPcOf(seg.chord), octave);
1459
+ return true;
1460
+ }
1461
+ const fifthPc = fifthPcOf2(seg.chord);
1462
+ if (sounding(ctx, placeTone(ctx, fifthPc)) === sounding(ctx, ctx.rootMidi)) {
1463
+ return false;
1464
+ }
1465
+ emit(ctx, pos, fifthPc);
1466
+ return true;
1467
+ }
1468
+ function octavePickup(ctx) {
1469
+ const root = sounding(ctx, ctx.rootMidi);
1470
+ for (const candidate of [ctx.rootMidi - 12, ctx.rootMidi + 12]) {
1471
+ if (candidate >= 0 && candidate <= 127 && sounding(ctx, candidate) !== root) {
1472
+ return candidate;
1473
+ }
1474
+ }
1475
+ return void 0;
1476
+ }
1189
1477
  function reachableLeap(ctx, pos, semitones) {
1190
1478
  const previous = ctx.notes[ctx.notes.length - 1];
1191
1479
  const available = previous === void 0 ? Number.POSITIVE_INFINITY : pos - previous.startBeat;
1192
1480
  return sustainsShift(semitones, available, ctx.bpm, ctx.difficulty);
1193
1481
  }
1194
1482
  function buildArpeggio(ctx, seg) {
1195
- const tones = chordTonePcs(seg.chord);
1483
+ const tones = bassToneCycle(seg.chord);
1196
1484
  const positions = beatPositions(seg.startBeat, seg.endBeat, ctx.ts);
1197
1485
  positions.forEach((pos, i) => {
1198
1486
  const pc = _nullishCoalesce(tones[i % tones.length], () => ( bassPcOf(seg.chord)));
1199
- ctx.prevMidi = emit(ctx, pos, pc);
1487
+ emit(ctx, pos, pc);
1200
1488
  });
1201
1489
  }
1202
1490
  function approachNote2(ctx, target, from, index) {
1203
- return approachNote(target, from, ctx.low, ctx.key, ctx.draw.prob(0.5, "approach", index));
1491
+ return approachNote(target, from, ctx.key, ctx.draw.prob(0.5, "approach", index));
1204
1492
  }
1205
1493
  function buildWalking(ctx, seg, next, index) {
1206
- const tones = chordTonePcs(seg.chord);
1494
+ const tones = bassToneCycle(seg.chord);
1207
1495
  const positions = beatPositions(seg.startBeat, seg.endBeat, ctx.ts);
1208
1496
  const count = positions.length;
1209
1497
  positions.forEach((pos, i) => {
1210
1498
  if (i === 0) {
1211
- ctx.prevMidi = emit(ctx, pos, bassPcOf(seg.chord));
1499
+ emit(ctx, pos, bassPcOf(seg.chord));
1212
1500
  return;
1213
1501
  }
1214
1502
  if (next && count > 1 && i === count - 1) {
1215
- const nextBass = placePc(bassPcOf(next.chord), ctx.prevMidi, ctx.low);
1503
+ const nextBass = placeRoot(bassPcOf(next.chord), ctx.low);
1216
1504
  const midi = approachNote2(ctx, nextBass, ctx.prevMidi, index);
1217
- ctx.prevMidi = emit(ctx, pos, _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, midi), midi);
1505
+ emit(ctx, pos, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, midi), midi);
1218
1506
  return;
1219
1507
  }
1220
1508
  const pc = _nullishCoalesce(tones[i % tones.length], () => ( bassPcOf(seg.chord)));
1221
- ctx.prevMidi = emit(ctx, pos, pc);
1509
+ emit(ctx, pos, pc);
1222
1510
  });
1223
1511
  }
1224
1512
  function generateBassLine(opts) {
1225
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, opts.segments.length, "bass segments", opts.budget);
1226
- for (let index = 0; index < opts.segments.length; index += 1) {
1227
- const segment = opts.segments[index];
1228
- if (!segment) continue;
1229
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, segment.startBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].startBeat`);
1230
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, segment.endBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].endBeat`);
1231
- if (segment.endBeat <= segment.startBeat) {
1232
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`segments[${index}] must have a positive duration`);
1233
- }
1234
- }
1235
- const segments = opts.segments.map((segment) => ({ ...segment, chord: _chunkUADYAYATcjs.toChordData.call(void 0, segment.chord) })).sort((a, b) => a.startBeat - b.startBeat);
1236
- for (let index = 1; index < segments.length; index += 1) {
1237
- const previous = segments[index - 1];
1238
- const current = segments[index];
1239
- if (previous !== void 0 && current !== void 0 && current.startBeat < previous.endBeat) {
1240
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1241
- `bass segments must not overlap: segments ${index - 1} and ${index} overlap`
1242
- );
1243
- }
1244
- }
1513
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "bass line options");
1514
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, opts.segments, "bass segments");
1515
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, opts.segments.length, "bass segments", opts.budget);
1516
+ const segments = opts.segments.map((segment) => ({ ...segment, chord: _chunk3R3QVKY5cjs.toChordData.call(void 0, segment.chord) })).sort((a, b) => a.startBeat - b.startBeat);
1517
+ assertBassSegments(segments);
1245
1518
  if (segments.length === 0) {
1246
1519
  return [];
1247
1520
  }
1248
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS2));
1249
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
1250
- const style = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.style, () => ( DEFAULT_STYLE)), BASS_STYLES, "bass style");
1251
- const octave = _nullishCoalesce(opts.octave, () => ( DEFAULT_OCTAVE2));
1252
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, octave, "bass octave", -1, 8);
1521
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS)), "ts"));
1522
+ const style = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.style, () => ( DEFAULT_STYLE)), BASS_STYLES, "bass style");
1523
+ const octave = _nullishCoalesce(opts.octave, () => ( DEFAULT_OCTAVE));
1524
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, octave, "bass octave", -1, 8);
1253
1525
  const estimatedNotes = segments.reduce(
1254
1526
  (count, segment) => count + Math.max(1, Math.ceil(segment.endBeat - segment.startBeat)),
1255
1527
  0
1256
1528
  );
1257
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, estimatedNotes, "bass notes", opts.budget);
1529
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, estimatedNotes, "bass notes", opts.budget);
1258
1530
  const resolved = resolveContext(opts.ctx);
1259
- const named = resolved.instrument("bass");
1260
- const instrument = _nullishCoalesce(opts.instrument, () => ( (named !== void 0 && named.kind === "stringed" ? named : void 0)));
1261
- const low = bandFloor(octave * 12 + 12, instrument);
1531
+ const { instrument, low } = bassRegister(resolved.instrument("bass"), opts.instrument, octave);
1262
1532
  const ctx = {
1263
1533
  ts,
1264
1534
  low,
1265
- key: _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key),
1535
+ key: _chunk3R3QVKY5cjs.toKeyScale.call(void 0, opts.key),
1266
1536
  draw: resolved.part("bass"),
1267
1537
  notes: [],
1538
+ rootMidi: low,
1539
+ overBass: false,
1268
1540
  prevMidi: low,
1269
1541
  instrument,
1270
1542
  pickupDensity: _nullishCoalesce(resolved.rhythmic, () => ( DEFAULT_PICKUP_DENSITY)),
@@ -1276,6 +1548,8 @@ function generateBassLine(opts) {
1276
1548
  if (!seg) {
1277
1549
  continue;
1278
1550
  }
1551
+ ctx.rootMidi = placeRoot(bassPcOf(seg.chord), ctx.low);
1552
+ ctx.overBass = seg.chord.bassPc !== void 0;
1279
1553
  switch (style) {
1280
1554
  case "rootFifth":
1281
1555
  buildRootFifth(ctx, seg);
@@ -1302,13 +1576,13 @@ function generateBassLine(opts) {
1302
1576
  if (!note2) {
1303
1577
  continue;
1304
1578
  }
1305
- const nextStart = _nullishCoalesce(_optionalChain([ctx, 'access', _19 => _19.notes, 'access', _20 => _20[i + 1], 'optionalAccess', _21 => _21.startBeat]), () => ( lastEnd));
1579
+ const nextStart = _nullishCoalesce(_optionalChain([ctx, 'access', _17 => _17.notes, 'access', _18 => _18[i + 1], 'optionalAccess', _19 => _19.startBeat]), () => ( lastEnd));
1306
1580
  const durationBeat = nextStart - note2.startBeat;
1307
- if (durationBeat <= EPS) {
1581
+ if (durationBeat <= _chunkS5BKZZGMcjs.BEAT_EPS) {
1308
1582
  continue;
1309
1583
  }
1310
1584
  out.push({
1311
- pitch: _chunkQ2WGKJNNcjs.clampToMidi.call(void 0, note2.pitch, "generated bass pitch"),
1585
+ pitch: _chunkS5BKZZGMcjs.clampToMidi.call(void 0, note2.pitch, "generated bass pitch"),
1312
1586
  startBeat: note2.startBeat,
1313
1587
  durationBeat,
1314
1588
  velocity: note2.velocity
@@ -1325,32 +1599,33 @@ function degreesOf(interval) {
1325
1599
  }
1326
1600
  function assertPlayable(pitch) {
1327
1601
  if (pitch < 0 || pitch > 127) {
1328
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`imitated pitch ${pitch} is outside the MIDI range 0..127`);
1602
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`imitated pitch ${pitch} is outside the MIDI range 0..127`);
1329
1603
  }
1330
1604
  return pitch;
1331
1605
  }
1332
1606
  function imitate(lead, opts) {
1333
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, lead, "imitate lead", { allowNonPositiveDuration: true });
1334
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, opts.atBeat, "atBeat");
1335
- const interval = _chunkQ2WGKJNNcjs.toSpelledInterval.call(void 0, opts.interval);
1336
- const key = _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key);
1607
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, lead, "imitate lead", { allowNonPositiveDuration: true });
1608
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
1609
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, opts.atBeat, "atBeat");
1610
+ const interval = _chunkS5BKZZGMcjs.toSpelledInterval.call(void 0, opts.interval);
1611
+ const key = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, opts.key);
1337
1612
  const from = _nullishCoalesce(opts.from, () => ( Number.NEGATIVE_INFINITY));
1338
1613
  const to = _nullishCoalesce(opts.to, () => ( Number.POSITIVE_INFINITY));
1339
1614
  if (opts.from !== void 0) {
1340
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, opts.from, "from");
1615
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, opts.from, "from");
1341
1616
  }
1342
1617
  if (opts.to !== void 0) {
1343
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, opts.to, "to");
1618
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, opts.to, "to");
1344
1619
  }
1345
1620
  if (from > to) {
1346
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`imitate needs from <= to; received ${from} > ${to}`);
1621
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`imitate needs from <= to; received ${from} > ${to}`);
1347
1622
  }
1348
1623
  const velocityScale = _nullishCoalesce(opts.velocityScale, () => ( 1));
1349
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, velocityScale, "velocityScale");
1350
- const tonal = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.answer, () => ( "real")), IMITATION_ANSWERS, "imitate answer") === "tonal";
1351
- const subject = _chunkQ2WGKJNNcjs.dropSilentNotes.call(void 0, lead).filter((note2) => note2.startBeat >= from && note2.startBeat < to).sort((a, b) => a.startBeat - b.startBeat);
1352
- const origin = _optionalChain([subject, 'access', _22 => _22[0], 'optionalAccess', _23 => _23.startBeat]);
1353
- const pivot = _optionalChain([subject, 'access', _24 => _24[0], 'optionalAccess', _25 => _25.pitch]);
1624
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, velocityScale, "velocityScale");
1625
+ const tonal = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.answer, () => ( "real")), IMITATION_ANSWERS, "imitate answer") === "tonal";
1626
+ const subject = _chunkS5BKZZGMcjs.dropSilentNotes.call(void 0, lead).filter((note2) => note2.startBeat >= from && note2.startBeat < to).sort((a, b) => a.startBeat - b.startBeat);
1627
+ const origin = _optionalChain([subject, 'access', _20 => _20[0], 'optionalAccess', _21 => _21.startBeat]);
1628
+ const pivot = _optionalChain([subject, 'access', _22 => _22[0], 'optionalAccess', _23 => _23.pitch]);
1354
1629
  if (origin === void 0 || pivot === void 0) {
1355
1630
  return [];
1356
1631
  }
@@ -1362,18 +1637,18 @@ function imitate(lead, opts) {
1362
1637
  let offset = 0;
1363
1638
  if (invert) {
1364
1639
  if (tonal) {
1365
- const here = _chunkUADYAYATcjs.scaleLadderPosition.call(void 0, pitch, key);
1366
- pitch = _chunkUADYAYATcjs.scaleLadderPitch.call(void 0, 2 * _chunkUADYAYATcjs.scaleLadderPosition.call(void 0, pivot, key).rung - here.rung, key);
1640
+ const here = _chunk3R3QVKY5cjs.scaleLadderPosition.call(void 0, pitch, key);
1641
+ pitch = _chunk3R3QVKY5cjs.scaleLadderPitch.call(void 0, 2 * _chunk3R3QVKY5cjs.scaleLadderPosition.call(void 0, pivot, key).rung - here.rung, key);
1367
1642
  offset = -here.offset;
1368
1643
  } else {
1369
1644
  pitch = 2 * pivot - pitch;
1370
1645
  }
1371
1646
  }
1372
- pitch = tonal ? _chunkUADYAYATcjs.shiftByScaleDegrees.call(void 0, pitch, degrees, key) + offset : pitch + Math.round(interval.semitones);
1647
+ pitch = tonal ? _chunk3R3QVKY5cjs.shiftByScaleDegrees.call(void 0, pitch, degrees, key) + offset : pitch + Math.round(interval.semitones);
1373
1648
  const answer = {
1649
+ ...note2,
1374
1650
  pitch: assertPlayable(pitch),
1375
- startBeat: note2.startBeat + shift,
1376
- durationBeat: note2.durationBeat
1651
+ startBeat: note2.startBeat + shift
1377
1652
  };
1378
1653
  if (note2.velocity !== void 0) {
1379
1654
  answer.velocity = Math.max(1, Math.min(127, Math.round(note2.velocity * velocityScale)));
@@ -1383,8 +1658,6 @@ function imitate(lead, opts) {
1383
1658
  }
1384
1659
 
1385
1660
  // src/generate/countermelody/index.ts
1386
- var DEFAULT_TS3 = { numerator: 4, denominator: 4 };
1387
- var EPS3 = 1e-9;
1388
1661
  var GRID_STEP = 0.5;
1389
1662
  var MIN_ONSET_GAP = 1;
1390
1663
  var REGISTER_OFFSET = 12;
@@ -1396,10 +1669,10 @@ var DEFAULT_MELODY_VELOCITY = 96;
1396
1669
  var VELOCITY_DROP = 16;
1397
1670
  var TIE_BREAK_JITTER = 1e-6;
1398
1671
  function isWholeBeat(beat) {
1399
- return Math.abs(beat - Math.round(beat)) < EPS3;
1672
+ return Math.abs(beat - Math.round(beat)) < _chunkS5BKZZGMcjs.BEAT_EPS;
1400
1673
  }
1401
1674
  function melodyNoteAt(melody, beat) {
1402
- return _optionalChain([melody, 'access', _26 => _26.at, 'call', _27 => _27(beat), 'optionalAccess', _28 => _28.note]);
1675
+ return _optionalChain([melody, 'access', _24 => _24.at, 'call', _25 => _25(beat), 'optionalAccess', _26 => _26.note]);
1403
1676
  }
1404
1677
  function melodyAttacksAt(melody, beat) {
1405
1678
  return melody.attacksAt(beat);
@@ -1409,8 +1682,8 @@ function melodySnapshotAt(melody, beat) {
1409
1682
  if (current === void 0) {
1410
1683
  return void 0;
1411
1684
  }
1412
- const attacked = Math.abs(current.startBeat - beat) < EPS3;
1413
- const previous = attacked ? melodyNoteAt(melody, beat - EPS3 * 2) : current;
1685
+ const attacked = Math.abs(current.startBeat - beat) < _chunkS5BKZZGMcjs.BEAT_EPS;
1686
+ const previous = attacked ? melodyNoteAt(melody, beat - _chunkS5BKZZGMcjs.BEAT_EPS * 2) : current;
1414
1687
  const snapshot = { pitch: current.pitch };
1415
1688
  if (previous !== void 0) {
1416
1689
  snapshot.prevPitch = previous.pitch;
@@ -1420,22 +1693,24 @@ function melodySnapshotAt(melody, beat) {
1420
1693
  function followOnsets(melody) {
1421
1694
  return [...new Set(melody.notes.map(({ note: note2 }) => note2.startBeat))];
1422
1695
  }
1696
+ var DEFAULT_TS2 = { numerator: 4, denominator: 4 };
1697
+ var SCORE_EPS = 1e-9;
1423
1698
  function complementOnsets(melody, ts, spanStart, spanEnd, draw) {
1424
1699
  const onsets = [];
1425
1700
  const gridStart = Math.floor(spanStart / GRID_STEP) * GRID_STEP;
1426
1701
  let last = Number.NEGATIVE_INFINITY;
1427
- for (let beat = gridStart; beat < spanEnd - EPS3; beat += GRID_STEP) {
1702
+ for (let beat = gridStart; beat < spanEnd - _chunkS5BKZZGMcjs.BEAT_EPS; beat += GRID_STEP) {
1428
1703
  const attacked = melodyAttacksAt(melody, beat);
1429
- const sounding = melodyNoteAt(melody, beat) !== void 0;
1704
+ const sounding2 = melodyNoteAt(melody, beat) !== void 0;
1430
1705
  let place = false;
1431
- if (!attacked && !sounding) {
1706
+ if (!attacked && !sounding2) {
1432
1707
  place = isWholeBeat(beat);
1433
1708
  } else if (!attacked) {
1434
1709
  place = draw.prob(isWholeBeat(beat) ? HOLD_ON_BEAT_PROB : HOLD_OFF_BEAT_PROB, "hold", beat);
1435
- } else if (_chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, beat, ts)) {
1710
+ } else if (_chunkS5BKZZGMcjs.isStrongBeat.call(void 0, beat, ts)) {
1436
1711
  place = draw.prob(REINFORCE_PROB, "reinforce", beat);
1437
1712
  }
1438
- if (place && beat - last >= MIN_ONSET_GAP - EPS3) {
1713
+ if (place && beat - last >= MIN_ONSET_GAP - _chunkS5BKZZGMcjs.BEAT_EPS) {
1439
1714
  onsets.push(beat);
1440
1715
  last = beat;
1441
1716
  }
@@ -1451,6 +1726,19 @@ function keepsImperfectInterval(before, now) {
1451
1726
  }
1452
1727
  return before <= 4 === now <= 4;
1453
1728
  }
1729
+ function assertWeightOverrides(overrides) {
1730
+ if (overrides === void 0) {
1731
+ return;
1732
+ }
1733
+ if (typeof overrides !== "object" || overrides === null) {
1734
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
1735
+ `countermelody weights must be an object of weight overrides; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, overrides)}`
1736
+ );
1737
+ }
1738
+ for (const [field, value] of Object.entries(overrides)) {
1739
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, value, `countermelody weights.${field}`);
1740
+ }
1741
+ }
1454
1742
  function scoreCandidate(pitch, melPitch, melPrev, prevPitch, chord, center, weights) {
1455
1743
  let score = 0;
1456
1744
  const ic = melPitch === void 0 ? void 0 : Math.abs(pitch - melPitch) % 12;
@@ -1463,7 +1751,7 @@ function scoreCandidate(pitch, melPitch, melPrev, prevPitch, chord, center, weig
1463
1751
  score += weights.dissonance;
1464
1752
  }
1465
1753
  }
1466
- if (chord && _chunkUADYAYATcjs.chordToneRole.call(void 0, pitch, chord) !== null) {
1754
+ if (chord && _chunk3R3QVKY5cjs.chordToneRole.call(void 0, pitch, chord) !== null) {
1467
1755
  score += weights.chordTone;
1468
1756
  }
1469
1757
  if (prevPitch !== void 0) {
@@ -1473,7 +1761,7 @@ function scoreCandidate(pitch, melPitch, melPrev, prevPitch, chord, center, weig
1473
1761
  if (dist > 0 && dist <= 2) {
1474
1762
  score += weights.stepwise;
1475
1763
  }
1476
- if (_chunkUADYAYATcjs.isForbiddenMelodicLeap.call(void 0, prevPitch, pitch)) {
1764
+ if (_chunk3R3QVKY5cjs.isForbiddenMelodicLeap.call(void 0, prevPitch, pitch)) {
1477
1765
  score += weights.forbiddenLeap;
1478
1766
  }
1479
1767
  if (melPitch !== void 0 && melPrev !== void 0 && ic !== void 0) {
@@ -1498,12 +1786,12 @@ function heldNoteBoundaries(melody, startBeat, endBeat, chordChangeBeats) {
1498
1786
  boundaries.add(onset);
1499
1787
  }
1500
1788
  for (const beat of chordChangeBeats) {
1501
- if (beat > startBeat + EPS3 && beat < endBeat - EPS3) {
1789
+ if (beat > startBeat + _chunkS5BKZZGMcjs.BEAT_EPS && beat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
1502
1790
  boundaries.add(beat);
1503
1791
  }
1504
1792
  }
1505
1793
  const firstGrid = Math.floor(startBeat / GRID_STEP + 1) * GRID_STEP;
1506
- for (let beat = firstGrid; beat < endBeat - EPS3; beat += GRID_STEP) {
1794
+ for (let beat = firstGrid; beat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS; beat += GRID_STEP) {
1507
1795
  boundaries.add(beat);
1508
1796
  }
1509
1797
  return [...boundaries].sort((a, b) => a - b);
@@ -1516,8 +1804,8 @@ function boundaryContexts(melody, harmony, startBeat, endBeat, ts) {
1516
1804
  melodyVoice,
1517
1805
  otherVoices: melodyVoice === void 0 ? [] : [melodyVoice],
1518
1806
  chord: harmony.chordAt(boundary),
1519
- strongBeat: _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, boundary, ts),
1520
- atCounterOnset: Math.abs(boundary - startBeat) < EPS3
1807
+ strongBeat: _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, boundary, ts),
1808
+ atCounterOnset: Math.abs(boundary - startBeat) < _chunkS5BKZZGMcjs.BEAT_EPS
1521
1809
  };
1522
1810
  });
1523
1811
  }
@@ -1528,7 +1816,7 @@ function heldPitchSafety(pitch, contexts, prevPitch, key, profile, register, low
1528
1816
  if (melodyVoice !== void 0 && (register === "below" && pitch >= melodyVoice.pitch || register === "above" && pitch <= melodyVoice.pitch)) {
1529
1817
  return null;
1530
1818
  }
1531
- const result = _chunkUADYAYATcjs.evaluateSafety.call(void 0,
1819
+ const result = _chunk3R3QVKY5cjs.evaluateSafety.call(void 0,
1532
1820
  {
1533
1821
  profile,
1534
1822
  candidatePitch: pitch,
@@ -1550,49 +1838,51 @@ function heldPitchSafety(pitch, contexts, prevPitch, key, profile, register, low
1550
1838
  return worst;
1551
1839
  }
1552
1840
  function generateCounterMelody(opts) {
1841
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "countermelody options");
1553
1842
  const timeline = opts.timeline;
1554
1843
  const chordAt = timeline === void 0 ? opts.chordAt : (beat) => timeline.at(beat);
1555
1844
  if (chordAt === void 0) {
1556
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("countermelody needs a timeline or a chordAt callback");
1845
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("countermelody needs a timeline or a chordAt callback");
1557
1846
  }
1558
1847
  const chordChangeBeats = timeline === void 0 ? _nullishCoalesce(opts.chordChangeBeats, () => ( [])) : timeline.segments.flatMap((segment) => [segment.startBeat, segment.endBeat]);
1559
1848
  const register = _nullishCoalesce(opts.register, () => ( "below"));
1560
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, opts.melody, "countermelody melody", { allowNonPositiveDuration: true });
1561
- const melody = _chunkK4ZJUGF4cjs.createNoteEventIndex.call(void 0, _chunkQ2WGKJNNcjs.soundingNotesOnly.call(void 0, opts.melody), {
1849
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, opts.melody, "countermelody melody", { allowNonPositiveDuration: true });
1850
+ const melody = _chunk35NBV2FTcjs.createNoteEventIndex.call(void 0, _chunkS5BKZZGMcjs.soundingNotesOnly.call(void 0, opts.melody), {
1562
1851
  tieBreak: register === "below" ? "highest" : "lowest"
1563
1852
  });
1564
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS3));
1565
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
1566
- const rhythm = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.rhythm, () => ( "complement")), ["complement", "follow"], "rhythm");
1567
- const profile = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.profile, () => ( "pop")), ["strict", "pop"], "safety profile");
1568
- const weights = _chunkUADYAYATcjs.profileWeights.call(void 0, profile, opts.weights);
1569
- const key = _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key);
1853
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS2)), "ts"));
1854
+ _chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, ts);
1855
+ const rhythm = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.rhythm, () => ( "complement")), ["complement", "follow"], "rhythm");
1856
+ const profile = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.profile, () => ( "pop")), ["strict", "pop"], "safety profile");
1857
+ assertWeightOverrides(opts.weights);
1858
+ const weights = _chunk3R3QVKY5cjs.profileWeights.call(void 0, profile, opts.weights);
1859
+ const key = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, opts.key);
1570
1860
  const draw = resolveContext(opts.ctx).part("countermelody");
1571
1861
  if (melody.notes.length === 0) {
1572
1862
  return [];
1573
1863
  }
1574
- const spanStart = _nullishCoalesce(_optionalChain([melody, 'access', _29 => _29.notes, 'access', _30 => _30[0], 'optionalAccess', _31 => _31.note, 'access', _32 => _32.startBeat]), () => ( 0));
1864
+ const spanStart = _nullishCoalesce(_optionalChain([melody, 'access', _27 => _27.notes, 'access', _28 => _28[0], 'optionalAccess', _29 => _29.note, 'access', _30 => _30.startBeat]), () => ( 0));
1575
1865
  const spanEnd = melody.notes.reduce((end, n) => Math.max(end, n.endBeat), spanStart);
1576
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, Math.ceil((spanEnd - spanStart) / GRID_STEP), "countermelody grid");
1866
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, Math.ceil((spanEnd - spanStart) / GRID_STEP), "countermelody grid");
1577
1867
  const meanPitch = melody.notes.reduce((sum, n) => sum + n.note.pitch, 0) / melody.notes.length;
1578
1868
  const defaultCenter = register === "below" ? meanPitch - REGISTER_OFFSET : meanPitch + REGISTER_OFFSET;
1579
- const low = _chunkQ2WGKJNNcjs.clampToMidi.call(void 0,
1869
+ const low = _chunkS5BKZZGMcjs.clampToMidi.call(void 0,
1580
1870
  _nullishCoalesce(opts.pitchLow, () => ( Math.round(defaultCenter) - DEFAULT_HALF_RANGE)),
1581
1871
  "countermelody pitchLow"
1582
1872
  );
1583
- const high = _chunkQ2WGKJNNcjs.clampToMidi.call(void 0,
1873
+ const high = _chunkS5BKZZGMcjs.clampToMidi.call(void 0,
1584
1874
  _nullishCoalesce(opts.pitchHigh, () => ( Math.round(defaultCenter) + DEFAULT_HALF_RANGE)),
1585
1875
  "countermelody pitchHigh"
1586
1876
  );
1587
1877
  if (low > high) {
1588
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1878
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
1589
1879
  `countermelody pitchLow must not exceed pitchHigh; received ${low} > ${high}`
1590
1880
  );
1591
1881
  }
1592
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, high - low + 1, "countermelody pitch candidates");
1593
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, chordChangeBeats.length, "chord change beats");
1882
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, high - low + 1, "countermelody pitch candidates");
1883
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, chordChangeBeats.length, "chord change beats");
1594
1884
  for (let index = 0; index < chordChangeBeats.length; index += 1) {
1595
- _chunkQ2WGKJNNcjs.assertRange.call(void 0,
1885
+ _chunkS5BKZZGMcjs.assertRange.call(void 0,
1596
1886
  _nullishCoalesce(chordChangeBeats[index], () => ( Number.NaN)),
1597
1887
  0,
1598
1888
  Number.MAX_SAFE_INTEGER,
@@ -1602,7 +1892,7 @@ function generateCounterMelody(opts) {
1602
1892
  const center = (low + high) / 2;
1603
1893
  const onsets = rhythm === "follow" ? followOnsets(melody) : complementOnsets(melody, ts, spanStart, spanEnd, draw);
1604
1894
  const boundaryEstimate = onsets.length + Math.ceil((spanEnd - spanStart) / GRID_STEP) + melody.notes.length + chordChangeBeats.length;
1605
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, boundaryEstimate * (high - low + 1), "countermelody search");
1895
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, boundaryEstimate * (high - low + 1), "countermelody search");
1606
1896
  const jitter = Array.from(
1607
1897
  { length: high - low + 1 },
1608
1898
  (_, index) => draw.at("jitter", index) * TIE_BREAK_JITTER
@@ -1617,20 +1907,20 @@ function generateCounterMelody(opts) {
1617
1907
  const nextOnset = onsets[onsetIndex + 1];
1618
1908
  const endBeat = _nullishCoalesce(nextOnset, () => ( Math.max(spanEnd, beat + GRID_STEP)));
1619
1909
  const melNote = melodyNoteAt(melody, beat);
1620
- const melPitch = _optionalChain([melNote, 'optionalAccess', _33 => _33.pitch]);
1910
+ const melPitch = _optionalChain([melNote, 'optionalAccess', _31 => _31.pitch]);
1621
1911
  const melodyVoice = melodySnapshotAt(melody, beat);
1622
- const melPrev = _optionalChain([melodyVoice, 'optionalAccess', _34 => _34.prevPitch]);
1912
+ const melPrev = _optionalChain([melodyVoice, 'optionalAccess', _32 => _32.prevPitch]);
1623
1913
  const contexts = boundaryContexts(melody, { chordAt, chordChangeBeats }, beat, endBeat, ts);
1624
- const chord = _nullishCoalesce(_optionalChain([contexts, 'access', _35 => _35[0], 'optionalAccess', _36 => _36.chord]), () => ( chordAt(beat)));
1914
+ const chord = _nullishCoalesce(_optionalChain([contexts, 'access', _33 => _33[0], 'optionalAccess', _34 => _34.chord]), () => ( chordAt(beat)));
1625
1915
  const otherVoices = melodyVoice !== void 0 ? [melodyVoice] : [];
1626
- const candidates = _chunkUADYAYATcjs.enumerateSafePitches.call(void 0,
1916
+ const candidates = _chunk3R3QVKY5cjs.enumerateSafePitches.call(void 0,
1627
1917
  {
1628
1918
  profile,
1629
1919
  prevPitch,
1630
1920
  chord,
1631
1921
  key,
1632
1922
  otherVoices,
1633
- strongBeat: _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, beat, ts),
1923
+ strongBeat: _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, beat, ts),
1634
1924
  vocalLow: low,
1635
1925
  vocalHigh: high
1636
1926
  },
@@ -1657,13 +1947,13 @@ function generateCounterMelody(opts) {
1657
1947
  if (melPitch !== void 0) {
1658
1948
  if (prevPitch !== void 0 && melPrev !== void 0 && // Parallel octaves are the perfect-class-zero case of this predicate,
1659
1949
  // so a single check covers both parallel fifths and octaves.
1660
- _chunkUADYAYATcjs.createsParallelPerfect.call(void 0, prevPitch, candidate, melPrev, melPitch)) {
1950
+ _chunk3R3QVKY5cjs.createsParallelPerfect.call(void 0, prevPitch, candidate, melPrev, melPitch)) {
1661
1951
  continue;
1662
1952
  }
1663
1953
  }
1664
1954
  const score = scoreCandidate(candidate, melPitch, melPrev, prevPitch, chord, center, weights) + (_nullishCoalesce(jitter[candidate - low], () => ( 0)));
1665
- const tie = Math.abs(score - bestScore) <= EPS3 && candidate < (_nullishCoalesce(bestPitch, () => ( Number.POSITIVE_INFINITY)));
1666
- if (worstSafety < bestWorstSafety || worstSafety === bestWorstSafety && (score > bestScore + EPS3 || tie)) {
1955
+ const tie = Math.abs(score - bestScore) <= SCORE_EPS && candidate < (_nullishCoalesce(bestPitch, () => ( Number.POSITIVE_INFINITY)));
1956
+ if (worstSafety < bestWorstSafety || worstSafety === bestWorstSafety && (score > bestScore + SCORE_EPS || tie)) {
1667
1957
  bestWorstSafety = worstSafety;
1668
1958
  bestScore = score;
1669
1959
  bestPitch = candidate;
@@ -1676,10 +1966,10 @@ function generateCounterMelody(opts) {
1676
1966
  const pitch = bestPitch;
1677
1967
  const velocity = Math.max(
1678
1968
  1,
1679
- Math.min(127, Math.round((_nullishCoalesce(_optionalChain([melNote, 'optionalAccess', _37 => _37.velocity]), () => ( DEFAULT_MELODY_VELOCITY))) - VELOCITY_DROP))
1969
+ Math.min(127, Math.round((_nullishCoalesce(_optionalChain([melNote, 'optionalAccess', _35 => _35.velocity]), () => ( DEFAULT_MELODY_VELOCITY))) - VELOCITY_DROP))
1680
1970
  );
1681
1971
  out.push({
1682
- pitch: _chunkQ2WGKJNNcjs.clampToMidi.call(void 0, pitch),
1972
+ pitch: _chunkS5BKZZGMcjs.clampToMidi.call(void 0, pitch),
1683
1973
  startBeat: beat,
1684
1974
  durationBeat: endBeat - beat,
1685
1975
  velocity
@@ -1742,8 +2032,8 @@ function getGhostVelocity(section, beatPosition) {
1742
2032
  }
1743
2033
  return Math.max(0.25, Math.min(0.65, base));
1744
2034
  }
1745
- function getGhostProbabilityAtPosition(beat, sixteenthInBeat) {
1746
- const leadsIntoBackbeat = (beat === 0 || beat === 2) && sixteenthInBeat === 3;
2035
+ function getGhostProbabilityAtPosition(beat, sixteenthInBeat, leadIns) {
2036
+ const leadsIntoBackbeat = leadIns.includes(beat) && sixteenthInBeat === 3;
1747
2037
  return leadsIntoBackbeat ? 0.6 : 0.25;
1748
2038
  }
1749
2039
  function selectGhostPositions(mood) {
@@ -1973,6 +2263,12 @@ function shouldAddOpenHHAccent(section, beat, bar2, draw) {
1973
2263
  function footHiHatVelocity(draw, bar2, beat) {
1974
2264
  return draw.range(FHH_VEL_MIN, FHH_VEL_MAX, "fhhVelocity", bar2, beat);
1975
2265
  }
2266
+ function usesOffbeatOpenHiHat(style) {
2267
+ return style === "fourOnFloor";
2268
+ }
2269
+ function offbeatOpenHiHatChance(bpm) {
2270
+ return Math.max(0.15, Math.min(0.8, 45 / bpm));
2271
+ }
1976
2272
  function shouldUseRideForSection(section, style) {
1977
2273
  if (style === "rock" && section === "chorus") {
1978
2274
  return true;
@@ -1982,9 +2278,6 @@ function shouldUseRideForSection(section, style) {
1982
2278
  }
1983
2279
  return section === "bridge";
1984
2280
  }
1985
- function shouldUseBridgeCrossStick(section, beat) {
1986
- return section === "bridge" && (beat === 1 || beat === 3);
1987
- }
1988
2281
 
1989
2282
  // src/generate/drums/swing.ts
1990
2283
  var SIXTEENTH2 = 0.25;
@@ -2072,36 +2365,50 @@ function generateKickForBeat(ctx, sec, kick) {
2072
2365
  ctx.track.add(GM.BD, tick, offBeat ? SIXTEENTH : EIGHTH, ctx.velocity * 0.85);
2073
2366
  }
2074
2367
  }
2075
- function generateSnareForBeat(ctx, sec, isIntroFirst) {
2076
- if (ctx.inPrechorusLift) {
2077
- return;
2368
+ function roleBackbeatWeight(role) {
2369
+ if (role === "minimal") {
2370
+ return 0.65;
2078
2371
  }
2079
- const snareOn = sec.snareBeat3 ? ctx.beat === 2 : ctx.beat === 1 || ctx.beat === 3;
2080
- if (!snareOn || isIntroFirst) {
2081
- return;
2372
+ return role === "ambient" ? 0.8 : 1;
2373
+ }
2374
+ function backbeatVoice(sec) {
2375
+ if (sec.role === "fxOnly") {
2376
+ return void 0;
2082
2377
  }
2083
- if (sec.useRide && shouldUseBridgeCrossStick(ctx.section, ctx.beat)) {
2084
- return;
2378
+ const sideStick = sec.style === "latin" || sec.role === "ambient" || sec.role === "minimal";
2379
+ return sideStick ? GM.SIDESTICK : GM.SD;
2380
+ }
2381
+ function writesSnareGhosts(sec) {
2382
+ return backbeatVoice(sec) === GM.SD && sec.style !== "sparse";
2383
+ }
2384
+ function backbeatStroke(sec, velocity) {
2385
+ const pitch = backbeatVoice(sec);
2386
+ if (pitch === void 0) {
2387
+ return void 0;
2085
2388
  }
2086
- const backbeatVel = Math.min(127, ctx.velocity + BACKBEAT_LIFT);
2087
- const promoteSparseChorus = sec.style === "sparse" && ctx.section === "chorus" && sec.role === "full";
2088
- if (sec.role === "fxOnly") {
2389
+ return pitch === GM.SD ? { pitch, velocity: Math.min(127, velocity + BACKBEAT_LIFT) } : { pitch, velocity: velocity * roleBackbeatWeight(sec.role) };
2390
+ }
2391
+ var FOUR_FOUR_BEATS = 4;
2392
+ function generateSnareForBeat(ctx, sec, isIntroFirst) {
2393
+ if (ctx.inPrechorusLift) {
2089
2394
  return;
2090
2395
  }
2091
- if (sec.role === "minimal") {
2092
- ctx.track.add(GM.SIDESTICK, ctx.beatTick, EIGHTH, ctx.velocity * 0.65);
2396
+ if (!backbeatBeats(sec.snareBeat3).includes(ctx.beat) || isIntroFirst) {
2093
2397
  return;
2094
2398
  }
2095
- if (promoteSparseChorus) {
2096
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, backbeatVel);
2097
- } else if (sec.style === "sparse" || sec.role === "ambient") {
2098
- ctx.track.add(GM.SIDESTICK, ctx.beatTick, EIGHTH, ctx.velocity * 0.8);
2099
- } else {
2100
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, backbeatVel);
2399
+ const stroke = backbeatStroke(sec, ctx.velocity);
2400
+ if (!stroke) {
2401
+ return;
2101
2402
  }
2403
+ ctx.track.add(stroke.pitch, ctx.beatTick, EIGHTH, stroke.velocity);
2102
2404
  }
2103
2405
  function generateGhostNotesForBeat(ctx, sec) {
2104
- if (ctx.beat !== 0 && ctx.beat !== 2) {
2406
+ if (!writesSnareGhosts(sec)) {
2407
+ return;
2408
+ }
2409
+ const backbeats = backbeatBeats(sec.snareBeat3);
2410
+ const leadIns = backbeats.map((beat) => (beat - 1 + FOUR_FOUR_BEATS) % FOUR_FOUR_BEATS);
2411
+ if (!leadIns.includes(ctx.beat)) {
2105
2412
  return;
2106
2413
  }
2107
2414
  if (!sustainsStrokes(SIXTEENTH, ctx.bpm, sec.difficulty)) {
@@ -2114,7 +2421,7 @@ function generateGhostNotesForBeat(ctx, sec) {
2114
2421
  }
2115
2422
  for (const pos of positions) {
2116
2423
  const sixteenthInBeat = pos === "e" ? 1 : 3;
2117
- const posProb = getGhostProbabilityAtPosition(ctx.beat, sixteenthInBeat);
2424
+ const posProb = getGhostProbabilityAtPosition(ctx.beat, sixteenthInBeat, leadIns);
2118
2425
  if (!ctx.draw.prob(ghostProb * posProb, "ghost", ctx.bar, ctx.beat, pos)) {
2119
2426
  continue;
2120
2427
  }
@@ -2154,40 +2461,47 @@ function generatePreChorusBuildup(ctx, sec, isSectionLastBar) {
2154
2461
  );
2155
2462
  }
2156
2463
  if (isSectionLastBar && ctx.beat === 3) {
2157
- ctx.track.add(GM.CRASH, ctx.beatTick + EIGHTH + SIXTEENTH, SIXTEENTH, ctx.velocity * 1.1);
2464
+ ctx.track.add(
2465
+ GM.CRASH,
2466
+ swing16(ctx.beatTick + EIGHTH + SIXTEENTH, sec, ctx.swingAmount),
2467
+ SIXTEENTH,
2468
+ ctx.velocity * 1.1
2469
+ );
2470
+ }
2471
+ }
2472
+ function opensOnDownbeat(ctx, sec) {
2473
+ if (usesOffbeatOpenHiHat(sec.style)) {
2474
+ return false;
2158
2475
  }
2476
+ return shouldAddOpenHHAccent(ctx.section, ctx.beat, ctx.bar, ctx.draw);
2477
+ }
2478
+ function opensOffbeat(ctx, sec) {
2479
+ if (!usesOffbeatOpenHiHat(sec.style) || ctx.beat !== 1 && ctx.beat !== 3) {
2480
+ return false;
2481
+ }
2482
+ return ctx.draw.prob(offbeatOpenHiHatChance(ctx.bpm), "openHat", ctx.bar, ctx.beat, 1);
2159
2483
  }
2160
2484
  function generateHiHatForBeat(ctx, sec) {
2161
2485
  if (!shouldPlayHiHat(sec.role)) {
2162
2486
  return;
2163
2487
  }
2164
- const crossStick = sec.useRide && shouldUseBridgeCrossStick(ctx.section, ctx.beat);
2165
- const hhInstrument = crossStick ? GM.SIDESTICK : roleHiHatInstrument(sec.role, sec.useRide);
2488
+ const hhInstrument = roleHiHatInstrument(sec.role, sec.useRide);
2166
2489
  const hhType = sectionHiHatType(ctx.section, sec.role);
2167
2490
  const typeMult = hiHatTypeVelocityMultiplier(hhType);
2168
- const isDynamicOpen = ctx.barHasOpenHh && ctx.beat === ctx.openHhBeat;
2169
2491
  const allowsOpenHiHat = sec.role !== "ambient" && sec.role !== "minimal";
2170
- const dm = sec.densityMult;
2171
- if (ctx.hhLevel === "quarter") {
2172
- const introRest = ctx.section === "intro" && ctx.beat !== 0;
2173
- if (!introRest) {
2174
- if (isDynamicOpen && allowsOpenHiHat) {
2175
- ctx.track.add(
2176
- GM.OHH,
2177
- ctx.beatTick,
2178
- EIGHTH,
2179
- ctx.velocity * dm * 0.75 * typeMult + OHH_VEL_BOOST
2180
- );
2181
- } else {
2182
- ctx.track.add(
2183
- hhInstrument,
2184
- ctx.beatTick,
2185
- EIGHTH,
2186
- Math.max(20, ctx.velocity * dm * 0.75 * typeMult)
2187
- );
2188
- }
2189
- } else if (sec.useFootHh) {
2190
- ctx.track.add(GM.FHH, ctx.beatTick, EIGHTH, footHiHatVelocity(ctx.draw, ctx.bar, ctx.beat));
2492
+ const isDynamicOpen = ctx.barHasOpenHh && ctx.beat === ctx.openHhBeat && !usesOffbeatOpenHiHat(sec.style);
2493
+ const dm = sec.densityMult;
2494
+ if (ctx.hhLevel === "quarter") {
2495
+ if (ctx.section === "intro" && ctx.beat !== 0) {
2496
+ return;
2497
+ }
2498
+ const hhVel = Math.max(20, ctx.velocity * dm * 0.75 * typeMult);
2499
+ if (allowsOpenHiHat && isDynamicOpen) {
2500
+ ctx.track.add(GM.OHH, ctx.beatTick, EIGHTH, hhVel + OHH_VEL_BOOST);
2501
+ } else if (allowsOpenHiHat && opensOnDownbeat(ctx, sec)) {
2502
+ ctx.track.add(hiHatNote("open"), ctx.beatTick, EIGHTH, Math.max(20, hhVel * 1.1));
2503
+ } else {
2504
+ ctx.track.add(hhInstrument, ctx.beatTick, EIGHTH, hhVel);
2191
2505
  }
2192
2506
  return;
2193
2507
  }
@@ -2208,13 +2522,7 @@ function generateHiHatForBeat(ctx, sec) {
2208
2522
  ctx.track.add(GM.OHH, hhTick, EIGHTH, hhVel + OHH_VEL_BOOST);
2209
2523
  continue;
2210
2524
  }
2211
- let useOpen = false;
2212
- if (sec.style === "fourOnFloor" && eighth === 1) {
2213
- const openProb = Math.max(0.15, Math.min(0.8, 45 / ctx.bpm));
2214
- useOpen = (ctx.beat === 1 || ctx.beat === 3) && ctx.draw.prob(openProb, "openHat", ctx.bar, ctx.beat, eighth);
2215
- } else if (eighth === 0) {
2216
- useOpen = shouldAddOpenHHAccent(ctx.section, ctx.beat, ctx.bar, ctx.draw);
2217
- }
2525
+ const useOpen = eighth === 0 ? opensOnDownbeat(ctx, sec) : opensOffbeat(ctx, sec);
2218
2526
  if (useOpen && allowsOpenHiHat) {
2219
2527
  ctx.track.add(hiHatNote("open"), hhTick, EIGHTH, Math.max(20, hhVel * 1.1));
2220
2528
  } else {
@@ -2237,6 +2545,14 @@ function generateHiHatForBeat(ctx, sec) {
2237
2545
  ctx.track.add(GM.OHH, hhTick, SIXTEENTH, hhVel + OHH_VEL_BOOST);
2238
2546
  continue;
2239
2547
  }
2548
+ if (allowsOpenHiHat && sixteenth === 0 && opensOnDownbeat(ctx, sec)) {
2549
+ ctx.track.add(hiHatNote("open"), hhTick, SIXTEENTH, Math.max(20, hhVel * 1.1));
2550
+ continue;
2551
+ }
2552
+ if (allowsOpenHiHat && sixteenth === 2 && opensOffbeat(ctx, sec)) {
2553
+ ctx.track.add(hiHatNote("open"), hhTick, SIXTEENTH, Math.max(20, hhVel * 1.1));
2554
+ continue;
2555
+ }
2240
2556
  if (allowsOpenHiHat && ctx.beat === 3 && sixteenth === 3) {
2241
2557
  const openProb = Math.max(0.1, Math.min(0.4, 30 / ctx.bpm));
2242
2558
  if (ctx.draw.prob(openProb, "openHat", ctx.bar, ctx.beat, sixteenth)) {
@@ -2250,10 +2566,10 @@ function generateHiHatForBeat(ctx, sec) {
2250
2566
 
2251
2567
  // src/generate/drums/euclid.ts
2252
2568
  function euclideanRhythm(pulses, steps, rotation = 0) {
2253
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, steps, "euclidean steps");
2254
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, pulses, "euclidean pulses");
2255
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, rotation, "euclidean rotation");
2256
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, steps, "euclidean pattern steps");
2569
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, steps, "euclidean steps");
2570
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, pulses, "euclidean pulses");
2571
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, rotation, "euclidean rotation");
2572
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, steps, "euclidean pattern steps");
2257
2573
  const p = Math.max(0, Math.min(pulses, steps));
2258
2574
  const base = bjorklund(p, steps);
2259
2575
  const shift = (rotation % steps + steps) % steps;
@@ -2329,7 +2645,7 @@ function archetype(beat2, beat3, boundToBar) {
2329
2645
  ...boundToBar ? { boundToBar } : {}
2330
2646
  };
2331
2647
  }
2332
- var FILL_ARCHETYPES = Object.freeze({
2648
+ var FILL_ARCHETYPES = deepFreeze({
2333
2649
  // One crescendo of seven strokes across the two beats, not two of four and
2334
2650
  // three: the roll is a single gesture and its second half continues where its
2335
2651
  // first half left off.
@@ -2423,7 +2739,7 @@ function isFillArchetype(material) {
2423
2739
  return typeof material === "object" && material !== null && Array.isArray(material.atBeat);
2424
2740
  }
2425
2741
  function fillArchetypeFor(id, supplied) {
2426
- return _nullishCoalesce(_optionalChain([supplied, 'optionalAccess', _38 => _38.get, 'call', _39 => _39(id)]), () => ( FILL_ARCHETYPES[id]));
2742
+ return _nullishCoalesce(_optionalChain([supplied, 'optionalAccess', _36 => _36.get, 'call', _37 => _37(id)]), () => ( FILL_ARCHETYPES[id]));
2427
2743
  }
2428
2744
  function getFillStartBeat(energy) {
2429
2745
  switch (energy) {
@@ -2432,8 +2748,9 @@ function getFillStartBeat(energy) {
2432
2748
  case "medium":
2433
2749
  return 2;
2434
2750
  case "high":
2751
+ return 1;
2435
2752
  case "peak":
2436
- return 2;
2753
+ return 0;
2437
2754
  }
2438
2755
  }
2439
2756
  var SPARSE_FILLS = ["simpleCrash", "breakdownFill"];
@@ -2543,7 +2860,7 @@ function fillWithinCeiling(shape, fromBeat, barBeats, bpm, difficulty) {
2543
2860
  }
2544
2861
  return true;
2545
2862
  }
2546
- function generateFill(track, beatTick, beat, fillType, velocity) {
2863
+ function generateFill(track, beatTick, beat, fillType, velocity, place = (tick) => tick) {
2547
2864
  const shape = typeof fillType === "string" ? FILL_ARCHETYPES[fillType] : fillType;
2548
2865
  if (!shape) {
2549
2866
  return;
@@ -2556,7 +2873,7 @@ function generateFill(track, beatTick, beat, fillType, velocity) {
2556
2873
  const scaled = stroke.velocity.scale === void 0 ? base : base * stroke.velocity.scale;
2557
2874
  track.add(
2558
2875
  stroke.voice,
2559
- beatTick + stroke.offset,
2876
+ place(beatTick + stroke.offset),
2560
2877
  stroke.duration,
2561
2878
  scaled + (_nullishCoalesce(stroke.velocity.offset, () => ( 0))),
2562
2879
  stroke.articulation
@@ -2584,17 +2901,30 @@ var DRUM_NOTES = Object.freeze({
2584
2901
  var VOICE_BY_NOTE = new Map(
2585
2902
  Object.entries(DRUM_NOTES).map(([voice, note2]) => [note2, voice])
2586
2903
  );
2904
+ function drumNoteOf(voice) {
2905
+ return Object.hasOwn(DRUM_NOTES, voice) ? DRUM_NOTES[voice] : void 0;
2906
+ }
2587
2907
  function drumVoiceOf(pitch) {
2588
- return VOICE_BY_NOTE.get(pitch);
2908
+ return VOICE_BY_NOTE.get(_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch));
2589
2909
  }
2590
2910
  var ONSET_KEY_SCALE = 128;
2591
2911
  var HitList = (_class = class {constructor() { _class.prototype.__init.call(this); }
2592
2912
  __init() {this.hits = []}
2593
- /** Onset keys already occupied, as `pitch:quantizedBeat`. */
2594
- #onsets = /* @__PURE__ */ new Set();
2595
- /** Append a hit, clamping velocity and dropping non-positive positions. */
2913
+ /** Voices already struck at each quantized beat. */
2914
+ #onsets = /* @__PURE__ */ new Map();
2915
+ /**
2916
+ * Append a hit, clamping velocity and dropping non-positive positions.
2917
+ *
2918
+ * A voice already struck at this instant is left as it stands: one drum
2919
+ * sounds once however many voices ask for it, and a second entry at the same
2920
+ * pitch and position is a note-on a reader answers with no note-off, heard as
2921
+ * a hung or cut stroke rather than as a louder one.
2922
+ */
2596
2923
  add(pitch, startBeat, durationBeat, velocity, articulation) {
2597
2924
  const beat = Math.max(0, startBeat);
2925
+ if (this.hasOnset(pitch, beat)) {
2926
+ return;
2927
+ }
2598
2928
  const hit2 = {
2599
2929
  pitch,
2600
2930
  startBeat: beat,
@@ -2605,23 +2935,34 @@ var HitList = (_class = class {constructor() { _class.prototype.__init.call(this
2605
2935
  hit2.articulation = articulation;
2606
2936
  }
2607
2937
  this.hits.push(hit2);
2608
- this.#onsets.add(onsetKey(pitch, beat));
2938
+ const struck = this.#onsets.get(beatSlot(beat));
2939
+ if (struck) {
2940
+ struck.add(pitch);
2941
+ } else {
2942
+ this.#onsets.set(beatSlot(beat), /* @__PURE__ */ new Set([pitch]));
2943
+ }
2609
2944
  }
2610
2945
  /** Whether a hit of this pitch already starts exactly at `startBeat`. */
2611
2946
  hasOnset(pitch, startBeat) {
2612
- return this.#onsets.has(onsetKey(pitch, startBeat));
2947
+ return _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.#onsets, 'access', _39 => _39.get, 'call', _40 => _40(beatSlot(startBeat)), 'optionalAccess', _41 => _41.has, 'call', _42 => _42(pitch)]), () => ( false));
2613
2948
  }
2614
- /** True when a crash already sits within a 16th of `startBeat`. */
2949
+ /**
2950
+ * True when a crash already sits less than a 16th from `startBeat`, on either
2951
+ * side of it.
2952
+ */
2615
2953
  hasCrashNear(startBeat) {
2616
2954
  const steps = Math.round(0.25 * ONSET_KEY_SCALE);
2617
- for (let step = 0; step < steps; step += 1) {
2618
- if (this.#onsets.has(onsetKey(GM.CRASH, startBeat + step / ONSET_KEY_SCALE))) {
2955
+ for (let step = -steps + 1; step < steps; step += 1) {
2956
+ if (this.hasOnset(GM.CRASH, startBeat + step / ONSET_KEY_SCALE)) {
2619
2957
  return true;
2620
2958
  }
2621
2959
  }
2622
2960
  return false;
2623
2961
  }
2624
2962
  }, _class);
2963
+ function beatSlot(startBeat) {
2964
+ return Math.round(startBeat * ONSET_KEY_SCALE);
2965
+ }
2625
2966
  function onsetKey(pitch, startBeat) {
2626
2967
  return `${pitch}:${Math.round(startBeat * ONSET_KEY_SCALE)}`;
2627
2968
  }
@@ -2637,7 +2978,13 @@ var BEAT_3 = beatStep(2);
2637
2978
  var BEAT_4 = beatStep(3);
2638
2979
  var AND_2 = beatStep(1) + 2;
2639
2980
  var AND_4 = beatStep(3) + 2;
2981
+ var A_OF_3 = beatStep(2) + 3;
2640
2982
  var OUTRO_FIGURE = [{ step: BEAT_1 }, { step: BEAT_3 }];
2983
+ function statedSlots(figure) {
2984
+ return figure.filter(
2985
+ (slot) => slot.probability === void 0 && slot.sections === void 0 && slot.barParity === void 0
2986
+ );
2987
+ }
2641
2988
  var DEFAULT_FIGURE = [
2642
2989
  { step: BEAT_1 },
2643
2990
  { step: BEAT_3 },
@@ -2645,7 +2992,7 @@ var DEFAULT_FIGURE = [
2645
2992
  { step: AND_2, probability: 0.55, slot: "beat2and", sections: ["chorus"] },
2646
2993
  { step: AND_4, probability: 0.35, slot: "beat4and", sections: ["chorus"] }
2647
2994
  ];
2648
- var KICK_FIGURES = Object.freeze({
2995
+ var KICK_FIGURES = deepFreeze({
2649
2996
  sparse: [{ step: BEAT_1 }, { step: BEAT_3, sections: ["chorus"], barParity: 1 }],
2650
2997
  fourOnFloor: [
2651
2998
  { step: BEAT_1 },
@@ -2663,9 +3010,13 @@ var KICK_FIGURES = Object.freeze({
2663
3010
  rock: [
2664
3011
  { step: BEAT_1 },
2665
3012
  { step: BEAT_3 },
2666
- { step: AND_2, probability: 0.65, slot: "beat2and", sections: ["chorus"] },
2667
- { step: AND_4, probability: 0.4, slot: "beat4and", sections: ["chorus"] },
2668
- { step: AND_2, probability: 0.3, slot: "beat2and", sections: ["prechorus"] }
3013
+ // The broken beat: the "and" of two answers the snare and the "a" of three
3014
+ // pushes into the fourth, in every section. That displacement is the figure
3015
+ // rather than a slot a dial may or may not fill in a chorus, which is what
3016
+ // separates it from the plain pop pulse wherever it is played.
3017
+ { step: AND_2 },
3018
+ { step: A_OF_3 },
3019
+ { step: AND_4, probability: 0.4, slot: "beat4and", sections: ["chorus"] }
2669
3020
  ],
2670
3021
  synth: [
2671
3022
  { step: BEAT_1 },
@@ -2719,7 +3070,8 @@ function realiseKickFigure(figure, section, bar2, draw, rhythmic) {
2719
3070
  return pattern;
2720
3071
  }
2721
3072
  function getKickPattern(section, style, bar2, draw, rhythmic) {
2722
- const figure = section === "outro" ? OUTRO_FIGURE : _nullishCoalesce(KICK_FIGURES[style], () => ( DEFAULT_FIGURE));
3073
+ const own = _nullishCoalesce(KICK_FIGURES[style], () => ( DEFAULT_FIGURE));
3074
+ const figure = section === "outro" ? [...OUTRO_FIGURE, ...statedSlots(own)] : own;
2723
3075
  return realiseKickFigure(figure, section, bar2, draw, rhythmic);
2724
3076
  }
2725
3077
 
@@ -2759,7 +3111,7 @@ var PERC_TABLE = [
2759
3111
  ];
2760
3112
  function getPercussionConfig(mood, section) {
2761
3113
  const row = _nullishCoalesce(PERC_TABLE[mood], () => ( PERC_TABLE[1 /* Standard */]));
2762
- const act = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _40 => _40[sectionIndex(section)]]), () => ( F));
3114
+ const act = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _43 => _43[sectionIndex(section)]]), () => ( F));
2763
3115
  return {
2764
3116
  tambourine: act[0],
2765
3117
  shaker: act[1],
@@ -2771,8 +3123,9 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
2771
3123
  if (role === "minimal") {
2772
3124
  return;
2773
3125
  }
3126
+ const backbeats = backbeatBeats(sec.snareBeat3, barBeats);
2774
3127
  if (config.tambourine) {
2775
- for (let beat = 1; beat < barBeats; beat += 2) {
3128
+ for (const beat of backbeats) {
2776
3129
  const raw = 70 * densityMult * draw.float(0.9, 1.1, "tambourine", barStart, beat);
2777
3130
  track.add(GM.TAMBOURINE, barStart + beat, EIGHTH, Math.max(40, Math.min(90, raw)));
2778
3131
  }
@@ -2811,7 +3164,7 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
2811
3164
  }
2812
3165
  }
2813
3166
  if (config.handclap) {
2814
- for (let beat = 1; beat < barBeats; beat += 2) {
3167
+ for (const beat of backbeats) {
2815
3168
  const raw = 85 * densityMult * draw.float(0.9, 1.1, "handclap", barStart, beat);
2816
3169
  track.add(GM.HANDCLAP, barStart + beat, EIGHTH, Math.max(50, Math.min(100, raw)));
2817
3170
  }
@@ -2837,6 +3190,11 @@ var REACH_BY_VOICE = {
2837
3190
  lowTom: ["rightHand", "leftHand"],
2838
3191
  shaker: ["rightHand", "leftHand"]
2839
3192
  };
3193
+ var OVERDUB_VOICES = Object.freeze([
3194
+ "handClap",
3195
+ "tambourine",
3196
+ "shaker"
3197
+ ]);
2840
3198
  function reachByNote() {
2841
3199
  const reach = {};
2842
3200
  for (const [voice, limbs] of Object.entries(REACH_BY_VOICE)) {
@@ -2858,10 +3216,12 @@ var KIT_ARTICULATIONS = Object.freeze([
2858
3216
  var DRUM_KIT = Object.freeze({
2859
3217
  kind: "percussion",
2860
3218
  name: "drum kit",
2861
- limbs: _chunkE3F4AHAHcjs.LIMBS,
2862
- reach: Object.freeze(reachByNote()),
3219
+ limbs: _chunkCGBADIO5cjs.LIMBS,
3220
+ reach: deepFreeze(reachByNote()),
3221
+ overdub: deepFreeze(OVERDUB_VOICES.map((voice) => DRUM_NOTES[voice])),
2863
3222
  articulations: KIT_ARTICULATIONS,
2864
- polyphony: 4
3223
+ // One stroke per limb, and one more per voice dubbed over them.
3224
+ polyphony: _chunkCGBADIO5cjs.LIMBS.length + OVERDUB_VOICES.length
2865
3225
  });
2866
3226
 
2867
3227
  // src/generate/drums/vocabulary.ts
@@ -2869,6 +3229,15 @@ function isDrumPattern(material) {
2869
3229
  const candidate = material;
2870
3230
  return typeof candidate === "object" && candidate !== null && typeof candidate.steps === "number" && Array.isArray(candidate.strokes);
2871
3231
  }
3232
+ function assertDrumPattern(material, label) {
3233
+ material.strokes.forEach((stroke, index) => {
3234
+ if (typeof stroke !== "object" || stroke === null || typeof stroke.voice !== "string" || drumNoteOf(stroke.voice) === void 0) {
3235
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3236
+ `${label} strokes[${index}].voice must name a drum voice; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, _optionalChain([stroke, 'optionalAccess', _44 => _44.voice]))}`
3237
+ );
3238
+ }
3239
+ });
3240
+ }
2872
3241
  function hit(voice, step, velocity, extra) {
2873
3242
  return { voice, step, velocity, ...extra };
2874
3243
  }
@@ -2886,7 +3255,7 @@ function bar(strokes) {
2886
3255
  return { steps: BAR_STEPS, strokes };
2887
3256
  }
2888
3257
  var FOUR_FOUR2 = { numerator: 4, denominator: 4 };
2889
- var DRUM_PATTERNS = Object.freeze([
3258
+ var DRUM_PATTERNS = deepFreeze([
2890
3259
  {
2891
3260
  id: "motownBackbeat",
2892
3261
  genre: "motown",
@@ -3056,27 +3425,33 @@ var DEFAULT_VELOCITY = 100;
3056
3425
  var DEFAULT_BPM2 = 120;
3057
3426
  var MAX_STROKES_PER_BAR = 64;
3058
3427
  function placeDrumPattern(opts) {
3059
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, opts.bars, "drum pattern bars");
3060
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, opts.bars * MAX_STROKES_PER_BAR, "drum pattern hits", opts.budget);
3061
- const genre = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.genre, GENRES, "drum pattern genre");
3062
- const section = opts.section === void 0 ? void 0 : _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum pattern section");
3063
- const ts = _nullishCoalesce(opts.ts, () => ( FOUR_FOUR2));
3064
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts, "drum pattern time signature");
3428
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "drum pattern options");
3429
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, opts.bars, "drum pattern bars");
3430
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, opts.bars * MAX_STROKES_PER_BAR, "drum pattern hits", opts.budget);
3431
+ const genre = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.genre, GENRES, "drum pattern genre");
3432
+ const section = opts.section === void 0 ? void 0 : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum pattern section");
3433
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(opts.ts, () => ( FOUR_FOUR2)), "ts"));
3065
3434
  if (opts.velocity !== void 0) {
3066
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, opts.velocity, 1, 127, "drum pattern velocity");
3435
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, opts.velocity, 1, 127, "drum pattern velocity");
3067
3436
  }
3068
- const feel = opts.feel === void 0 ? "straight" : _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "feel");
3437
+ const feel = opts.feel === void 0 ? "straight" : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "feel");
3069
3438
  const resolved = resolveContext(opts.ctx);
3070
3439
  const bpm = _nullishCoalesce(resolved.bpm, () => ( DEFAULT_BPM2));
3071
3440
  const draw = resolved.part("drums");
3072
3441
  const baseVelocity = _nullishCoalesce(opts.velocity, () => ( DEFAULT_VELOCITY));
3073
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
3442
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
3074
3443
  const kit = resolved.instrument("drums");
3075
3444
  const swing = effectiveSwing(feel, feelSwingAmount(feel));
3076
- const dictionary = mergeVocabulary(
3077
- DRUM_PATTERNS,
3078
- vocabularyOfKind(resolved.vocabulary, isDrumPattern)
3079
- );
3445
+ const supplied = vocabularyOfKind(resolved.vocabulary, isDrumPattern);
3446
+ supplied.forEach((entry, index) => {
3447
+ assertDrumPattern(entry.material, `drum pattern vocabulary[${index}]`);
3448
+ });
3449
+ const dictionary = mergeVocabulary(DRUM_PATTERNS, supplied);
3450
+ if (selectVocabulary(dictionary, { ts }).length === 0) {
3451
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3452
+ `drum pattern ts names a meter no figure is written in: ${_chunkS5BKZZGMcjs.formatTimeSignature.call(void 0, ts)}. The built-in figures are written in 4/4; supply vocabulary in another meter through the context.`
3453
+ );
3454
+ }
3080
3455
  const track = new HitList();
3081
3456
  for (let bar2 = 0; bar2 < opts.bars; bar2 += 1) {
3082
3457
  const entry = pickVocabulary(
@@ -3106,7 +3481,10 @@ function placeDrumPattern(opts) {
3106
3481
  ...resolved.ornament === void 0 ? {} : { ornament: resolved.ornament },
3107
3482
  isOrnament: (stroke) => stroke.articulation === "ghost",
3108
3483
  rate: _nullishCoalesce(opts.rate, () => ( "straight")),
3109
- spanSteps: entry.material.steps
3484
+ spanSteps: entry.material.steps,
3485
+ // The dials rank the figure's positions against the bar it is written
3486
+ // in, which is the bar this call is placing it in.
3487
+ ts
3110
3488
  },
3111
3489
  draw,
3112
3490
  "pattern",
@@ -3117,13 +3495,18 @@ function placeDrumPattern(opts) {
3117
3495
  }
3118
3496
  const barStart = bar2 * barBeats;
3119
3497
  for (const stroke of strokes) {
3120
- const pitch = DRUM_NOTES[stroke.voice];
3498
+ const pitch = drumNoteOf(stroke.voice);
3121
3499
  if (pitch === void 0) {
3122
3500
  continue;
3123
3501
  }
3124
3502
  track.add(
3125
3503
  pitch,
3126
- quantizeSwing(barStart + stroke.step * STEP_BEATS, swing, "sixteenth"),
3504
+ // The bar's own grid is what is swung, and the bar start is added
3505
+ // back after. Swinging the absolute beat takes the offset inside a
3506
+ // quarter note, which is the same thing only while a bar is a whole
3507
+ // number of them: in 7/8 every other bar begins half a beat into one,
3508
+ // so the downbeat was warped and the offbeats came out straight.
3509
+ barStart + quantizeSwing(stroke.step * STEP_BEATS, swing, "sixteenth"),
3127
3510
  _nullishCoalesce(stroke.duration, () => ( STEP_BEATS)),
3128
3511
  baseVelocity * stroke.velocity,
3129
3512
  stroke.articulation
@@ -3131,25 +3514,26 @@ function placeDrumPattern(opts) {
3131
3514
  }
3132
3515
  }
3133
3516
  const endBeat = opts.bars * barBeats;
3134
- return track.hits.filter((h) => h.startBeat < endBeat && (kit === void 0 || _chunkE3F4AHAHcjs.canSound.call(void 0, kit, h.pitch))).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
3517
+ return track.hits.filter((h) => h.startBeat < endBeat && (kit === void 0 || _chunkCGBADIO5cjs.canSound.call(void 0, kit, h.pitch))).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
3135
3518
  }
3136
3519
 
3137
3520
  // src/generate/drums/index.ts
3138
3521
  var MAX_HITS_PER_BAR = 128;
3139
3522
  var DEFAULT_BPM3 = 120;
3140
3523
  function generateDrums(opts) {
3141
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, opts.bars, "drum bars");
3142
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, opts.bars * MAX_HITS_PER_BAR, "drum hits", opts.budget);
3524
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "drum options");
3525
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, opts.bars, "drum bars");
3526
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, opts.bars * MAX_HITS_PER_BAR, "drum hits", opts.budget);
3143
3527
  if (opts.euclideanKick) {
3144
3528
  const steps = _nullishCoalesce(opts.euclideanKick.steps, () => ( 16));
3145
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, steps, "euclidean steps", 1, 16);
3146
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, opts.euclideanKick.pulses, "euclidean pulses");
3147
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, _nullishCoalesce(opts.euclideanKick.rotation, () => ( 0)), "euclidean rotation");
3529
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, steps, "euclidean steps", 1, 16);
3530
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, opts.euclideanKick.pulses, "euclidean pulses");
3531
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(opts.euclideanKick.rotation, () => ( 0)), "euclidean rotation");
3148
3532
  }
3149
- const publicStyle = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.style, GROOVE_STYLES, "drum style");
3150
- const publicSection = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum section");
3533
+ const publicStyle = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.style, GROOVE_STYLES, "drum style");
3534
+ const publicSection = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum section");
3151
3535
  if (opts.nextSection !== void 0) {
3152
- _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.nextSection, PUBLIC_SECTIONS, "drum nextSection");
3536
+ _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.nextSection, PUBLIC_SECTIONS, "drum nextSection");
3153
3537
  }
3154
3538
  const track = new HitList();
3155
3539
  const resolved = resolveContext(opts.ctx);
@@ -3160,16 +3544,18 @@ function generateDrums(opts) {
3160
3544
  const difficulty = resolved.difficulty;
3161
3545
  const mapping = mapStyle(publicStyle);
3162
3546
  const style = mapping.style;
3163
- const feel = opts.feel === void 0 ? mapping.feel : _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "drum feel");
3164
- const role = opts.role === void 0 ? "full" : _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.role, DRUM_ROLES, "drum role");
3165
- const ts = _nullishCoalesce(opts.ts, () => ( { numerator: 4, denominator: 4 }));
3166
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts, "drum time signature");
3167
- if (ts.numerator !== 4 || ts.denominator !== 4) {
3168
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
3169
- `drum time signature must be 4/4; received ${ts.numerator}/${ts.denominator}`
3170
- );
3547
+ const feel = opts.feel === void 0 ? mapping.feel : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "drum feel");
3548
+ const role = opts.role === void 0 ? "full" : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.role, DRUM_ROLES, "drum role");
3549
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, { ts: opts.ts }, "ts");
3550
+ for (const change of meters) {
3551
+ if (change.ts.numerator !== 4 || change.ts.denominator !== 4) {
3552
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3553
+ `drum time signature must be 4/4 throughout; received ${change.ts.numerator}/${change.ts.denominator} at beat ${change.startBeat}`
3554
+ );
3555
+ }
3171
3556
  }
3172
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
3557
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, meters);
3558
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
3173
3559
  const playMainVoices = role !== "fxOnly";
3174
3560
  const section = mapSection(publicSection);
3175
3561
  const swingAmount = feelSwingAmount(feel);
@@ -3182,7 +3568,6 @@ function generateDrums(opts) {
3182
3568
  rhythmic,
3183
3569
  ornament: ornamentDial,
3184
3570
  difficulty,
3185
- useGhostNotes: (section === "b" || section === "chorus" || section === "bridge") && style !== "sparse",
3186
3571
  ghostBoost: mapping.ghostBoost,
3187
3572
  useRide: shouldUseRideForSection(section, style),
3188
3573
  useFootHh: shouldUseFootHiHat(section, role),
@@ -3203,7 +3588,7 @@ function generateDrums(opts) {
3203
3588
  );
3204
3589
  const callerFills = new Map(callerFillEntries.map((entry) => [entry.id, entry.material]));
3205
3590
  const callerFillIds = callerFillEntries.map((entry) => entry.id);
3206
- const euclidSteps = _nullishCoalesce(_optionalChain([opts, 'access', _41 => _41.euclideanKick, 'optionalAccess', _42 => _42.steps]), () => ( 16));
3591
+ const euclidSteps = _nullishCoalesce(_optionalChain([opts, 'access', _45 => _45.euclideanKick, 'optionalAccess', _46 => _46.steps]), () => ( 16));
3207
3592
  const euclidKick = opts.euclideanKick ? euclideanRhythm(opts.euclideanKick.pulses, euclidSteps, _nullishCoalesce(opts.euclideanKick.rotation, () => ( 0))) : void 0;
3208
3593
  const reuseSectionKick = (section === "b" || section === "chorus") && style !== "sparse";
3209
3594
  let sectionKick;
@@ -3253,7 +3638,14 @@ function generateDrums(opts) {
3253
3638
  if (playMainVoices) {
3254
3639
  const before = track.hits.length;
3255
3640
  if (currentFill) {
3256
- generateFill(track, beatTick, beat, currentFill, fillVelocity);
3641
+ generateFill(
3642
+ track,
3643
+ beatTick,
3644
+ beat,
3645
+ currentFill,
3646
+ fillVelocity,
3647
+ (tick) => swing16(tick, sec, swingAmount)
3648
+ );
3257
3649
  }
3258
3650
  if (track.hits.length > before) {
3259
3651
  continue;
@@ -3297,7 +3689,7 @@ function generateDrums(opts) {
3297
3689
  generateKickForBeat(ctx, sec, kick);
3298
3690
  }
3299
3691
  generateSnareForBeat(ctx, sec, section === "intro" && bar2 === 0);
3300
- if (sec.useGhostNotes && !inLift) {
3692
+ if (!inLift) {
3301
3693
  generateGhostNotesForBeat(ctx, sec);
3302
3694
  }
3303
3695
  }
@@ -3356,11 +3748,11 @@ function underCeiling(hits, bpm, difficulty, exempt) {
3356
3748
  return kept;
3357
3749
  }
3358
3750
  function playableOn(kit, pitch) {
3359
- return kit === void 0 || _chunkE3F4AHAHcjs.canSound.call(void 0, kit, pitch);
3751
+ return kit === void 0 || _chunkCGBADIO5cjs.canSound.call(void 0, kit, pitch);
3360
3752
  }
3361
3753
 
3362
3754
  // src/generate/groove/index.ts
3363
- var DEFAULT_TS4 = { numerator: 4, denominator: 4 };
3755
+ var DEFAULT_TS3 = { numerator: 4, denominator: 4 };
3364
3756
  var DEFAULT_PART = "humanize";
3365
3757
  var DEFAULT_GROOVE_SUBDIVISION = 4;
3366
3758
  var DEFAULT_TIMING = 0.02;
@@ -3371,24 +3763,24 @@ var MAX_METRIC_WEIGHT = 3;
3371
3763
  var MIN_VELOCITY = 1;
3372
3764
  var MAX_VELOCITY = 127;
3373
3765
  function humanize(events, opts = {}) {
3374
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS4));
3375
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
3376
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, events, "humanize events", { allowNonPositiveDuration: true });
3377
- const sounding = _chunkQ2WGKJNNcjs.dropSilentNotes.call(void 0, events);
3378
- const timing = _nullishCoalesce(opts.timing, () => ( DEFAULT_TIMING));
3379
- const velocityJitter = _nullishCoalesce(opts.velocity, () => ( DEFAULT_VELOCITY_JITTER));
3380
- const accent2 = _nullishCoalesce(opts.accent, () => ( DEFAULT_ACCENT));
3381
- const baseVelocity = _nullishCoalesce(opts.baseVelocity, () => ( DEFAULT_BASE_VELOCITY));
3382
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, timing, 0, Number.MAX_SAFE_INTEGER, "humanize timing");
3383
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, velocityJitter, 0, 127, "humanize velocity jitter");
3384
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, accent2, 0, 127, "humanize accent");
3385
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, baseVelocity, 0, 127, "humanize base velocity");
3386
- const ctx = resolveContext(opts.ctx);
3387
- const draw = ctx.part(_nullishCoalesce(opts.part, () => ( DEFAULT_PART)));
3388
- return sounding.map((event) => {
3766
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
3767
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(asked.ts, () => ( DEFAULT_TS3)), "ts"));
3768
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, "humanize events", { allowNonPositiveDuration: true });
3769
+ const sounding2 = _chunkS5BKZZGMcjs.dropSilentNotes.call(void 0, events);
3770
+ const timing = _nullishCoalesce(asked.timing, () => ( DEFAULT_TIMING));
3771
+ const velocityJitter = _nullishCoalesce(asked.velocity, () => ( DEFAULT_VELOCITY_JITTER));
3772
+ const accent2 = _nullishCoalesce(asked.accent, () => ( DEFAULT_ACCENT));
3773
+ const baseVelocity = _nullishCoalesce(asked.baseVelocity, () => ( DEFAULT_BASE_VELOCITY));
3774
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, timing, 0, Number.MAX_SAFE_INTEGER, "humanize timing");
3775
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, velocityJitter, 0, 127, "humanize velocity jitter");
3776
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, accent2, 0, 127, "humanize accent");
3777
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, baseVelocity, 0, 127, "humanize base velocity");
3778
+ const ctx = resolveContext(asked.ctx);
3779
+ const draw = ctx.part(_nullishCoalesce(asked.part, () => ( DEFAULT_PART)));
3780
+ return sounding2.map((event) => {
3389
3781
  const timingOffset = draw.float(-timing, timing, "timing", event.startBeat, event.pitch);
3390
3782
  const startBeat = event.startBeat + timingOffset;
3391
- const weight = _chunkK4ZJUGF4cjs.metricWeight.call(void 0, event.startBeat, ts);
3783
+ const weight = _chunkS5BKZZGMcjs.metricWeight.call(void 0, event.startBeat, ts);
3392
3784
  const accentBoost = weight / MAX_METRIC_WEIGHT * accent2;
3393
3785
  const velocityOffset = draw.float(
3394
3786
  -velocityJitter,
@@ -3399,12 +3791,7 @@ function humanize(events, opts = {}) {
3399
3791
  );
3400
3792
  const rawVelocity = (_nullishCoalesce(event.velocity, () => ( baseVelocity))) + accentBoost + velocityOffset;
3401
3793
  const velocity = Math.min(MAX_VELOCITY, Math.max(MIN_VELOCITY, Math.round(rawVelocity)));
3402
- return {
3403
- pitch: event.pitch,
3404
- startBeat,
3405
- durationBeat: event.durationBeat,
3406
- velocity
3407
- };
3794
+ return { ...event, startBeat, velocity };
3408
3795
  });
3409
3796
  }
3410
3797
  function quantizeToGrid(beat, barBeats, subdivision, slotsPerBar) {
@@ -3422,18 +3809,18 @@ function quantizeToGrid(beat, barBeats, subdivision, slotsPerBar) {
3422
3809
  };
3423
3810
  }
3424
3811
  function extractGrooveTemplate(events, ts, subdivision = DEFAULT_GROOVE_SUBDIVISION) {
3425
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
3426
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, events, "groove source events", { allowNonPositiveDuration: true });
3427
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, subdivision, "groove subdivision");
3428
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
3812
+ const meter = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
3813
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, "groove source events", { allowNonPositiveDuration: true });
3814
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, subdivision, "groove subdivision");
3815
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, meter);
3429
3816
  const slotsPerBar = Math.ceil(barBeats * subdivision);
3430
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, slotsPerBar, "groove template slots");
3431
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, slotsPerBar, "groove template slots");
3817
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, slotsPerBar, "groove template slots");
3818
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slotsPerBar, "groove template slots");
3432
3819
  const offsetSums = new Array(slotsPerBar).fill(0);
3433
3820
  const velocitySums = new Array(slotsPerBar).fill(0);
3434
3821
  const counts = new Array(slotsPerBar).fill(0);
3435
3822
  const velocityCounts = new Array(slotsPerBar).fill(0);
3436
- for (const event of _chunkQ2WGKJNNcjs.dropSilentNotes.call(void 0, events)) {
3823
+ for (const event of _chunkS5BKZZGMcjs.dropSilentNotes.call(void 0, events)) {
3437
3824
  const { quantizedBeat, slotIndex } = quantizeToGrid(
3438
3825
  event.startBeat,
3439
3826
  barBeats,
@@ -3460,24 +3847,40 @@ function extractGrooveTemplate(events, ts, subdivision = DEFAULT_GROOVE_SUBDIVIS
3460
3847
  velocity: velocityCount > 0 ? (_nullishCoalesce(velocitySums[i], () => ( 0))) / velocityCount : null
3461
3848
  });
3462
3849
  }
3463
- return { subdivision, slotsPerBar, slots, ts };
3850
+ return { subdivision, slotsPerBar, slots, ts: meter };
3464
3851
  }
3465
3852
  function applyGrooveTemplate(events, template, ts) {
3466
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
3467
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, events, "groove target events", { allowNonPositiveDuration: true });
3468
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, template.subdivision, "template subdivision");
3469
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, template.slotsPerBar, "template slotsPerBar");
3470
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, template.slots.length, "template slots");
3853
+ const meter = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
3854
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, "groove target events", { allowNonPositiveDuration: true });
3855
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, template.subdivision, "template subdivision");
3856
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, template.slotsPerBar, "template slotsPerBar");
3857
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, template.slots.length, "template slots");
3471
3858
  if (template.slots.length !== template.slotsPerBar) {
3472
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("template slots length must equal slotsPerBar");
3859
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("template slots length must equal slotsPerBar");
3860
+ }
3861
+ if (template.ts && (template.ts.numerator !== meter.numerator || template.ts.denominator !== meter.denominator)) {
3862
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3863
+ `Groove template meter ${_chunkS5BKZZGMcjs.formatTimeSignature.call(void 0, template.ts)} does not match apply-time meter ${_chunkS5BKZZGMcjs.formatTimeSignature.call(void 0, meter)}`
3864
+ );
3473
3865
  }
3474
- if (template.ts && (template.ts.numerator !== ts.numerator || template.ts.denominator !== ts.denominator)) {
3475
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
3476
- `Groove template meter ${_chunkK4ZJUGF4cjs.formatTimeSignature.call(void 0, template.ts)} does not match apply-time meter ${_chunkK4ZJUGF4cjs.formatTimeSignature.call(void 0, ts)}`
3866
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, meter);
3867
+ const expectedSlots = Math.ceil(barBeats * template.subdivision);
3868
+ if (template.slotsPerBar !== expectedSlots) {
3869
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
3870
+ `Groove template slotsPerBar ${template.slotsPerBar} does not match ${expectedSlots} slots per bar of ${_chunkS5BKZZGMcjs.formatTimeSignature.call(void 0, meter)} at subdivision ${template.subdivision}`
3477
3871
  );
3478
3872
  }
3479
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
3480
- return _chunkQ2WGKJNNcjs.dropSilentNotes.call(void 0, events).map((event) => {
3873
+ for (let index = 0; index < template.slots.length; index += 1) {
3874
+ const slot = template.slots[index];
3875
+ if (slot === void 0) {
3876
+ continue;
3877
+ }
3878
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, slot.timingOffset, `template slots[${index}].timingOffset`);
3879
+ if (slot.velocity !== null && slot.velocity !== void 0) {
3880
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, slot.velocity, 0, 127, `template slots[${index}].velocity`);
3881
+ }
3882
+ }
3883
+ return _chunkS5BKZZGMcjs.dropSilentNotes.call(void 0, events).map((event) => {
3481
3884
  const { quantizedBeat, slotIndex } = quantizeToGrid(
3482
3885
  event.startBeat,
3483
3886
  barBeats,
@@ -3485,22 +3888,16 @@ function applyGrooveTemplate(events, template, ts) {
3485
3888
  template.slotsPerBar
3486
3889
  );
3487
3890
  const slot = template.slots[slotIndex];
3488
- const startBeat = quantizedBeat + (_nullishCoalesce(_optionalChain([slot, 'optionalAccess', _43 => _43.timingOffset]), () => ( 0)));
3891
+ const startBeat = quantizedBeat + (_nullishCoalesce(_optionalChain([slot, 'optionalAccess', _47 => _47.timingOffset]), () => ( 0)));
3489
3892
  const velocity = slot && slot.velocity !== null && slot.velocity !== void 0 ? Math.round(slot.velocity) : event.velocity;
3490
- return {
3491
- pitch: event.pitch,
3492
- startBeat,
3493
- durationBeat: event.durationBeat,
3494
- velocity
3495
- };
3893
+ return { ...event, startBeat, velocity };
3496
3894
  });
3497
3895
  }
3498
3896
 
3499
3897
  // src/generate/harmonize/nct.ts
3500
- var ADJACENCY_TOLERANCE = 0.05;
3501
3898
  function snapToPulse(beat, pulse2) {
3502
3899
  const snapped = Math.round(beat / pulse2) * pulse2;
3503
- return Math.abs(snapped - beat) <= ADJACENCY_TOLERANCE ? snapped : beat;
3900
+ return Math.abs(snapped - beat) <= _chunkSGLEQVJHcjs.HUMANIZE_ADJACENCY ? snapped : beat;
3504
3901
  }
3505
3902
  var STEP_SEMITONES = 2;
3506
3903
  function isStep(a, b) {
@@ -3514,11 +3911,11 @@ function sameDirection(prev, note2, next) {
3514
3911
  return prev !== note2 && next !== note2 && note2 - prev > 0 === next - note2 > 0;
3515
3912
  }
3516
3913
  function classifyMelodyTones(melody, ts) {
3517
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, melody, "melody notes", { allowNonPositiveDuration: true });
3518
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
3519
- const pulse2 = _chunkK4ZJUGF4cjs.pulseBeats.call(void 0, ts);
3914
+ const meter = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
3915
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, melody, "melody notes", { allowNonPositiveDuration: true });
3916
+ const pulse2 = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, meter);
3520
3917
  return melody.map((_, index) => {
3521
- const role = roleOfNote(melody, index, ts, pulse2);
3918
+ const role = roleOfNote(melody, index, meter, pulse2);
3522
3919
  return { noteIndex: index, role, ornamental: role !== "structural" };
3523
3920
  });
3524
3921
  }
@@ -3531,12 +3928,12 @@ function roleOfNote(melody, index, ts, pulse2) {
3531
3928
  }
3532
3929
  const intoNote = Math.abs(prev.startBeat + prev.durationBeat - note2.startBeat);
3533
3930
  const outOfNote = Math.abs(note2.startBeat + note2.durationBeat - next.startBeat);
3534
- if (intoNote > ADJACENCY_TOLERANCE || outOfNote > ADJACENCY_TOLERANCE) {
3931
+ if (intoNote > _chunkSGLEQVJHcjs.HUMANIZE_ADJACENCY || outOfNote > _chunkSGLEQVJHcjs.HUMANIZE_ADJACENCY) {
3535
3932
  return "structural";
3536
3933
  }
3537
- const weight = _chunkK4ZJUGF4cjs.metricWeight.call(void 0, snapToPulse(note2.startBeat, pulse2), ts);
3538
- const weightPrev = _chunkK4ZJUGF4cjs.metricWeight.call(void 0, snapToPulse(prev.startBeat, pulse2), ts);
3539
- const weightNext = _chunkK4ZJUGF4cjs.metricWeight.call(void 0, snapToPulse(next.startBeat, pulse2), ts);
3934
+ const weight = _chunkS5BKZZGMcjs.metricWeight.call(void 0, snapToPulse(note2.startBeat, pulse2), ts);
3935
+ const weightPrev = _chunkS5BKZZGMcjs.metricWeight.call(void 0, snapToPulse(prev.startBeat, pulse2), ts);
3936
+ const weightNext = _chunkS5BKZZGMcjs.metricWeight.call(void 0, snapToPulse(next.startBeat, pulse2), ts);
3540
3937
  const subordinate = weight <= weightPrev && weight <= weightNext && note2.durationBeat <= prev.durationBeat && note2.durationBeat <= next.durationBeat;
3541
3938
  const leaning = weight > weightPrev && weight >= weightNext;
3542
3939
  if (leaning && prev.pitch === note2.pitch && note2.pitch - next.pitch >= 1 && isStep(note2.pitch, next.pitch)) {
@@ -3562,63 +3959,44 @@ function roleOfNote(melody, index, ts, pulse2) {
3562
3959
  return "structural";
3563
3960
  }
3564
3961
 
3565
- // src/generate/harmonize/index.ts
3962
+ // src/generate/harmonize/internal.ts
3566
3963
  var FALLBACK = {
3567
3964
  rootPc: 0,
3568
3965
  quality: "maj",
3569
3966
  secondaryDominant: false,
3570
3967
  base: 0,
3571
- pcs: _chunkUADYAYATcjs.chordPitchClasses.call(void 0, _chunkUADYAYATcjs.makeChord.call(void 0, 0, "maj"))
3968
+ pcs: _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, 0, "maj"))
3572
3969
  };
3573
3970
  function defaultHarmonicRhythm(ts) {
3574
- const bar2 = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
3971
+ const bar2 = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
3575
3972
  const half = bar2 / 2;
3576
- return half % _chunkK4ZJUGF4cjs.pulseBeats.call(void 0, ts) === 0 ? half : bar2;
3973
+ return half % _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts) === 0 ? half : bar2;
3577
3974
  }
3578
- var DEFAULT_PLACEMENT = { transposeSearch: false, octaveSearch: false };
3975
+ var DEFAULT_PLACEMENT = {
3976
+ transposeSearch: false,
3977
+ octaveSearch: false
3978
+ };
3579
3979
  var DEFAULT_METER = { numerator: 4, denominator: 4 };
3580
- var COMFORT_LOW = 55;
3581
- var COMFORT_HIGH = 79;
3582
- var TESSITURA_WEIGHT = 1e-3;
3583
- var NON_CHORD_TONE_STRONG = 4;
3584
- var NON_CHORD_TONE_WEAK = 1;
3585
- var NON_SCALE_TONE = 0.5;
3586
- var DEGREE_BASE = [0, 0.3, 0.45, 0.15, 0.1, 0.3, 0.9];
3587
- var SECONDARY_DOMINANT_BASE = 0.5;
3588
- var BORROWED_BASE = 0.5;
3589
- var CHORD_CHANGE = 0.8;
3590
- var DESCENDING_FIFTH = -0.4;
3591
- var ASCENDING_FIFTH = 0;
3592
- var DOMINANT_TO_TONIC = -0.3;
3593
- var SECONDARY_UNRESOLVED = 1;
3594
- var SECONDARY_AFTER_TARGET = 0.8;
3595
- var ROOT_MOTION_COST = [0, 0.15, 0.15, 0.2, 0.2, 0, 0.4];
3596
- var PHRASE_TONIC = -7;
3597
- var PHRASE_AUTHENTIC = -0.5;
3598
- var OUT_OF_KEY_PER_BEAT = 3;
3599
3980
  var TIE_BREAK_JITTER2 = 1e-6;
3600
- function isMinorKey(key) {
3601
- return (key.modeMask12 >> 3 & 1) === 1 && (key.modeMask12 >> 4 & 1) === 0;
3602
- }
3603
- function leadingTonePc(key) {
3604
- return (_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc) + 11) % 12;
3981
+ function cadencesThroughRaisedSeventh(key) {
3982
+ return _chunkSGLEQVJHcjs.isMinorKey.call(void 0, key) && _chunk3R3QVKY5cjs.scaleSystemOf.call(void 0, key) === "common-practice";
3605
3983
  }
3606
3984
  function isKeyTone(pitch, key) {
3607
- return _chunkUADYAYATcjs.isScaleTone.call(void 0, pitch, key) || isMinorKey(key) && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pitch) === leadingTonePc(key);
3985
+ return _chunk3R3QVKY5cjs.isScaleTone.call(void 0, pitch, key) || cadencesThroughRaisedSeventh(key) && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch) === _chunk3R3QVKY5cjs.leadingTonePcOf.call(void 0, key);
3608
3986
  }
3609
3987
  function inferKey(melody) {
3610
- let best = _chunkUADYAYATcjs.majorKey.call(void 0, 0);
3988
+ let best = _chunk3R3QVKY5cjs.majorKey.call(void 0, 0);
3611
3989
  let bestScore = Number.NEGATIVE_INFINITY;
3612
3990
  const candidates = [];
3613
3991
  for (let tonic = 0; tonic < 12; tonic += 1) {
3614
- candidates.push(_chunkUADYAYATcjs.majorKey.call(void 0, tonic), _chunkUADYAYATcjs.minorKey.call(void 0, tonic));
3992
+ candidates.push(_chunk3R3QVKY5cjs.majorKey.call(void 0, tonic), _chunk3R3QVKY5cjs.minorKey.call(void 0, tonic));
3615
3993
  }
3616
3994
  for (const key of candidates) {
3617
- const tonic = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc);
3995
+ const tonic = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
3618
3996
  let score = 0;
3619
3997
  for (const n of melody) {
3620
3998
  const w = Math.max(0.25, n.durationBeat);
3621
- const pc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, n.pitch);
3999
+ const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, n.pitch);
3622
4000
  if (isKeyTone(n.pitch, key)) {
3623
4001
  score += w;
3624
4002
  }
@@ -3633,99 +4011,27 @@ function inferKey(melody) {
3633
4011
  }
3634
4012
  return best;
3635
4013
  }
3636
- var SECONDARY_DOMINANT_TARGETS = [2, 4, 5, 6];
3637
- var SECONDARY_DOMINANT_ENTRY_ORDER = [5, 6, 2, 4];
3638
- var SECONDARY_DOMINANT_BAND = 0.5;
3639
- var REHARMONIZE_DIAL = {
3640
- diatonic: 0,
3641
- secondaryDominant: SECONDARY_DOMINANT_BAND,
3642
- borrowed: 1
3643
- };
3644
- function admittedCount(fraction, size) {
3645
- return Math.max(0, Math.min(size, Math.ceil(fraction * size)));
3646
- }
3647
- function buildCandidates(key, harmonic) {
3648
- const tones = _chunkUADYAYATcjs.scaleTonesInDegreeOrder.call(void 0, key);
3649
- const candidates = tones.map((rootPc, index) => {
3650
- const degree = index + 1;
3651
- const quality = _chunkUADYAYATcjs.diatonicTriad.call(void 0, degree, key).quality;
3652
- return {
3653
- rootPc,
3654
- quality,
3655
- degree,
3656
- secondaryDominant: false,
3657
- base: _nullishCoalesce(DEGREE_BASE[index], () => ( 0.6)),
3658
- pcs: _chunkUADYAYATcjs.chordPitchClasses.call(void 0, _chunkUADYAYATcjs.makeChord.call(void 0, rootPc, quality))
3659
- };
3660
- });
3661
- if (isMinorKey(key)) {
3662
- const rootPc = (_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc) + 7) % 12;
3663
- candidates.push({
3664
- rootPc,
3665
- quality: "maj",
3666
- degree: 5,
3667
- secondaryDominant: false,
3668
- base: _nullishCoalesce(DEGREE_BASE[4], () => ( 0.6)),
3669
- pcs: _chunkUADYAYATcjs.chordPitchClasses.call(void 0, _chunkUADYAYATcjs.makeChord.call(void 0, rootPc, "maj"))
3670
- });
3671
- }
3672
- const secondaryOpen = admittedCount(
3673
- harmonic / SECONDARY_DOMINANT_BAND,
3674
- SECONDARY_DOMINANT_TARGETS.length
3675
- );
3676
- for (const target of SECONDARY_DOMINANT_TARGETS) {
3677
- if (SECONDARY_DOMINANT_ENTRY_ORDER.indexOf(target) >= secondaryOpen) {
3678
- continue;
3679
- }
3680
- const targetRoot = _nullishCoalesce(tones[target - 1], () => ( 0));
3681
- const rootPc = (targetRoot + 7) % 12;
3682
- candidates.push({
3683
- rootPc,
3684
- quality: "dom7",
3685
- secondaryDominant: true,
3686
- targetDegree: target,
3687
- base: SECONDARY_DOMINANT_BASE,
3688
- pcs: _chunkUADYAYATcjs.chordPitchClasses.call(void 0, _chunkUADYAYATcjs.makeChord.call(void 0, rootPc, "dom7"))
3689
- });
3690
- }
3691
- const parallel = _chunkOE6WK2Y2cjs.parallelKey.call(void 0, key);
3692
- const parallelTones = _chunkUADYAYATcjs.scaleTonesInDegreeOrder.call(void 0, parallel);
3693
- const borrowed = [];
3694
- for (let degree = 1; degree <= parallelTones.length; degree += 1) {
3695
- const chord = _chunkUADYAYATcjs.diatonicTriad.call(void 0, degree, parallel);
3696
- if (!_chunkOE6WK2Y2cjs.isDiatonic.call(void 0, chord, key)) {
3697
- borrowed.push(chord);
3698
- }
3699
- }
3700
- const borrowedOpen = admittedCount(
3701
- (harmonic - SECONDARY_DOMINANT_BAND) / (1 - SECONDARY_DOMINANT_BAND),
3702
- borrowed.length
3703
- );
3704
- for (const chord of borrowed.slice(0, borrowedOpen)) {
3705
- candidates.push({
3706
- rootPc: chord.rootPc,
3707
- quality: chord.quality,
3708
- secondaryDominant: false,
3709
- base: BORROWED_BASE,
3710
- pcs: _chunkUADYAYATcjs.chordPitchClasses.call(void 0, chord)
3711
- });
3712
- }
3713
- const seen = /* @__PURE__ */ new Set();
3714
- return candidates.filter((candidate) => {
3715
- const id = `${candidate.rootPc}:${candidate.quality}`;
3716
- if (seen.has(id)) {
3717
- return false;
3718
- }
3719
- seen.add(id);
3720
- return true;
3721
- });
3722
- }
4014
+
4015
+ // src/generate/harmonize/cost.ts
4016
+ var NON_CHORD_TONE_STRONG = 4;
4017
+ var NON_CHORD_TONE_WEAK = 1;
4018
+ var NON_SCALE_TONE = 0.5;
4019
+ var CHORD_CHANGE = 0.8;
4020
+ var DESCENDING_FIFTH = -0.4;
4021
+ var ASCENDING_FIFTH = 0;
4022
+ var DOMINANT_TO_TONIC = -0.3;
4023
+ var SECONDARY_UNRESOLVED = 1;
4024
+ var SECONDARY_AFTER_TARGET = 0.8;
4025
+ var ROOT_MOTION_COST = [0, 0.15, 0.15, 0.2, 0.2, 0, 0.4];
4026
+ var PHRASE_TONIC = -7;
4027
+ var PHRASE_AUTHENTIC = -0.5;
4028
+ var OUT_OF_KEY_PER_BEAT = 3;
3723
4029
  function emissionCost(seg, cand, melody, key) {
3724
4030
  const pcs = cand.pcs;
3725
4031
  let cost = cand.base * seg.weight;
3726
4032
  for (const { index, weight, strong } of seg.costNotes) {
3727
4033
  const note2 = melody[index];
3728
- if (!note2 || pcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, note2.pitch))) {
4034
+ if (!note2 || pcs.includes(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, note2.pitch))) {
3729
4035
  continue;
3730
4036
  }
3731
4037
  cost += (strong ? NON_CHORD_TONE_STRONG : NON_CHORD_TONE_WEAK) * weight;
@@ -3735,40 +4041,9 @@ function emissionCost(seg, cand, melody, key) {
3735
4041
  }
3736
4042
  return cost;
3737
4043
  }
3738
- function tessituraCost(melody) {
3739
- let cost = 0;
3740
- for (const n of melody) {
3741
- if (n.pitch < COMFORT_LOW) {
3742
- cost += (COMFORT_LOW - n.pitch) * TESSITURA_WEIGHT;
3743
- } else if (n.pitch > COMFORT_HIGH) {
3744
- cost += (n.pitch - COMFORT_HIGH) * TESSITURA_WEIGHT;
3745
- }
3746
- }
3747
- return cost;
3748
- }
3749
4044
  function isDominantOf(cand, tonicPc) {
3750
4045
  return cand.rootPc === (tonicPc + 7) % 12 && (cand.quality === "maj" || cand.quality === "dom7");
3751
4046
  }
3752
- function keyFitCost(melody, key) {
3753
- let cost = 0;
3754
- for (const n of melody) {
3755
- if (!isKeyTone(n.pitch, key)) {
3756
- cost += Math.max(0.25, n.durationBeat) * OUT_OF_KEY_PER_BEAT;
3757
- }
3758
- }
3759
- return cost;
3760
- }
3761
- function mergeRepeats(spans) {
3762
- const merged = [];
3763
- for (const span of spans) {
3764
- const prev = merged.at(-1);
3765
- if (prev && prev.rootPc === span.rootPc && prev.quality === span.quality && prev.degree === span.degree && prev.secondaryDominant === span.secondaryDominant) {
3766
- continue;
3767
- }
3768
- merged.push(span);
3769
- }
3770
- return merged;
3771
- }
3772
4047
  function transitionCost(prev, cur, tonicPc) {
3773
4048
  let cost = prev.rootPc === cur.rootPc && prev.quality === cur.quality ? 0 : CHORD_CHANGE;
3774
4049
  const down = (prev.rootPc - cur.rootPc + 12) % 12;
@@ -3794,14 +4069,99 @@ function phraseEndBonus(prev, cur, tonicPc, seg, melody) {
3794
4069
  if (cur.rootPc !== tonicPc || seg.closingIndex === void 0) {
3795
4070
  return 0;
3796
4071
  }
3797
- const closing = melody[seg.closingIndex];
3798
- if (!closing || _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, closing.pitch) !== tonicPc) {
3799
- return 0;
4072
+ const closing = melody[seg.closingIndex];
4073
+ if (!closing || _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, closing.pitch) !== tonicPc) {
4074
+ return 0;
4075
+ }
4076
+ const weight = Math.max(seg.weight, seg.beats);
4077
+ return (PHRASE_TONIC + (prev !== null && isDominantOf(prev, tonicPc) ? PHRASE_AUTHENTIC : 0)) * weight;
4078
+ }
4079
+
4080
+ // src/generate/harmonize/key-fit.ts
4081
+ var COMFORT_LOW = 55;
4082
+ var COMFORT_HIGH = 79;
4083
+ var TESSITURA_WEIGHT = 1e-3;
4084
+ function tessituraCost(melody) {
4085
+ let cost = 0;
4086
+ for (const n of melody) {
4087
+ if (n.pitch < COMFORT_LOW) {
4088
+ cost += (COMFORT_LOW - n.pitch) * TESSITURA_WEIGHT;
4089
+ } else if (n.pitch > COMFORT_HIGH) {
4090
+ cost += (n.pitch - COMFORT_HIGH) * TESSITURA_WEIGHT;
4091
+ }
4092
+ }
4093
+ return cost;
4094
+ }
4095
+ function keyFitCost(melody, key) {
4096
+ let cost = 0;
4097
+ for (const n of melody) {
4098
+ if (!isKeyTone(n.pitch, key)) {
4099
+ cost += Math.max(0.25, n.durationBeat) * OUT_OF_KEY_PER_BEAT;
4100
+ }
4101
+ }
4102
+ return cost;
4103
+ }
4104
+
4105
+ // src/generate/harmonize/search.ts
4106
+ function sameHarmony(a, b) {
4107
+ return a.rootPc === b.rootPc && a.quality === b.quality;
4108
+ }
4109
+ function harmonizeOnce(melody, key, candidates, segments, jitter, phraseEnds, articulated) {
4110
+ const tonicPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
4111
+ const n = candidates.length;
4112
+ const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
4113
+ const seg0 = segments[0];
4114
+ let dp = candidates.map(
4115
+ (c, ci) => (seg0 ? emissionCost(seg0, c, melody, key) : 0) + (seg0 && phraseEnds.has(0) ? phraseEndBonus(null, c, tonicPc, seg0, melody) : 0) + (_nullishCoalesce(jitter[ci], () => ( 0)))
4116
+ );
4117
+ const back = [];
4118
+ for (let s = 1; s < segments.length; s += 1) {
4119
+ const seg = segments[s];
4120
+ if (!seg) {
4121
+ continue;
4122
+ }
4123
+ const closes = phraseEnds.has(s);
4124
+ const articulates = articulated.has(s);
4125
+ const next = [];
4126
+ const ptr = [];
4127
+ for (let c = 0; c < n; c += 1) {
4128
+ let best = Number.POSITIVE_INFINITY;
4129
+ let bestPrev = 0;
4130
+ for (let p = 0; p < n; p += 1) {
4131
+ if (articulates && sameHarmony(candAt(p), candAt(c))) {
4132
+ continue;
4133
+ }
4134
+ const cost = (_nullishCoalesce(dp[p], () => ( Number.POSITIVE_INFINITY))) + transitionCost(candAt(p), candAt(c), tonicPc) * seg.beats + (closes ? phraseEndBonus(candAt(p), candAt(c), tonicPc, seg, melody) : 0);
4135
+ if (cost < best) {
4136
+ best = cost;
4137
+ bestPrev = p;
4138
+ }
4139
+ }
4140
+ next[c] = best + emissionCost(seg, candAt(c), melody, key) + (_nullishCoalesce(jitter[c], () => ( 0)));
4141
+ ptr[c] = bestPrev;
4142
+ }
4143
+ dp = next;
4144
+ back.push(ptr);
4145
+ }
4146
+ let bestCost = Number.POSITIVE_INFINITY;
4147
+ let bestEnd = 0;
4148
+ for (let c = 0; c < n; c += 1) {
4149
+ const total = _nullishCoalesce(dp[c], () => ( Number.POSITIVE_INFINITY));
4150
+ if (total < bestCost) {
4151
+ bestCost = total;
4152
+ bestEnd = c;
4153
+ }
4154
+ }
4155
+ const path = new Array(segments.length).fill(bestEnd);
4156
+ for (let s = segments.length - 2; s >= 0; s -= 1) {
4157
+ const ptr = back[s];
4158
+ const nextIdx = _nullishCoalesce(path[s + 1], () => ( bestEnd));
4159
+ path[s] = ptr ? _nullishCoalesce(ptr[nextIdx], () => ( bestEnd)) : bestEnd;
3800
4160
  }
3801
- const weight = Math.max(seg.weight, seg.beats);
3802
- return (PHRASE_TONIC + (prev !== null && isDominantOf(prev, tonicPc) ? PHRASE_AUTHENTIC : 0)) * weight;
4161
+ return { cost: bestCost, path };
3803
4162
  }
3804
- var BEAT_EPS = 1e-9;
4163
+
4164
+ // src/generate/harmonize/segments.ts
3805
4165
  function gridBounds(segmentStart, hr, gridCount, ends) {
3806
4166
  const bounds = [];
3807
4167
  for (let s = 0; s <= gridCount; s += 1) {
@@ -3809,13 +4169,13 @@ function gridBounds(segmentStart, hr, gridCount, ends) {
3809
4169
  }
3810
4170
  const last = segmentStart + gridCount * hr;
3811
4171
  for (const end of ends) {
3812
- if (end > segmentStart + BEAT_EPS && end < last - BEAT_EPS) {
4172
+ if (end > segmentStart + _chunkS5BKZZGMcjs.BEAT_EPS && end < last - _chunkS5BKZZGMcjs.BEAT_EPS) {
3813
4173
  bounds.push(end);
3814
4174
  }
3815
4175
  }
3816
4176
  bounds.sort((a, b) => a - b);
3817
4177
  return bounds.filter(
3818
- (beat, index) => index === 0 || beat - (_nullishCoalesce(bounds[index - 1], () => ( beat))) > BEAT_EPS
4178
+ (beat, index) => index === 0 || beat - (_nullishCoalesce(bounds[index - 1], () => ( beat))) > _chunkS5BKZZGMcjs.BEAT_EPS
3819
4179
  );
3820
4180
  }
3821
4181
  function closingNoteIndices(spans, ends) {
@@ -3824,7 +4184,7 @@ function closingNoteIndices(spans, ends) {
3824
4184
  let index = -1;
3825
4185
  let start = Number.NEGATIVE_INFINITY;
3826
4186
  for (const [i, span] of spans.entries()) {
3827
- if (span.startBeat < end - BEAT_EPS && span.startBeat >= start) {
4187
+ if (span.startBeat < end - _chunkS5BKZZGMcjs.BEAT_EPS && span.startBeat >= start) {
3828
4188
  start = span.startBeat;
3829
4189
  index = i;
3830
4190
  }
@@ -3850,8 +4210,16 @@ function segmentIndexAt(bounds, beat) {
3850
4210
  }
3851
4211
  return found;
3852
4212
  }
3853
- function sameHarmony(a, b) {
3854
- return a.rootPc === b.rootPc && a.quality === b.quality;
4213
+ function mergeRepeats(spans) {
4214
+ const merged = [];
4215
+ for (const span of spans) {
4216
+ const prev = merged.at(-1);
4217
+ if (prev && prev.rootPc === span.rootPc && prev.quality === span.quality && prev.degree === span.degree && prev.secondaryDominant === span.secondaryDominant) {
4218
+ continue;
4219
+ }
4220
+ merged.push(span);
4221
+ }
4222
+ return merged;
3855
4223
  }
3856
4224
  function phraseEndSegments(segments, ends) {
3857
4225
  const closing = new Set(segments.length > 0 ? [segments.length - 1] : []);
@@ -3859,7 +4227,7 @@ function phraseEndSegments(segments, ends) {
3859
4227
  for (const end of ends) {
3860
4228
  let index = -1;
3861
4229
  for (let s = 0; s < segments.length; s += 1) {
3862
- if ((_nullishCoalesce(_optionalChain([segments, 'access', _44 => _44[s], 'optionalAccess', _45 => _45.startBeat]), () => ( 0))) < end - BEAT_EPS) {
4230
+ if ((_nullishCoalesce(_optionalChain([segments, 'access', _48 => _48[s], 'optionalAccess', _49 => _49.startBeat]), () => ( 0))) < end - _chunkS5BKZZGMcjs.BEAT_EPS) {
3863
4231
  index = s;
3864
4232
  }
3865
4233
  }
@@ -3870,103 +4238,19 @@ function phraseEndSegments(segments, ends) {
3870
4238
  }
3871
4239
  return { closing, named };
3872
4240
  }
3873
- function harmonizeOnce(melody, key, candidates, segments, jitter, phraseEnds, articulated) {
3874
- const tonicPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc);
3875
- const n = candidates.length;
3876
- const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
3877
- const seg0 = segments[0];
3878
- let dp = candidates.map(
3879
- (c, ci) => (seg0 ? emissionCost(seg0, c, melody, key) : 0) + (seg0 && phraseEnds.has(0) ? phraseEndBonus(null, c, tonicPc, seg0, melody) : 0) + (_nullishCoalesce(jitter[ci], () => ( 0)))
3880
- );
3881
- const back = [];
3882
- for (let s = 1; s < segments.length; s += 1) {
3883
- const seg = segments[s];
3884
- if (!seg) {
3885
- continue;
3886
- }
3887
- const closes = phraseEnds.has(s);
3888
- const articulates = articulated.has(s);
3889
- const next = [];
3890
- const ptr = [];
3891
- for (let c = 0; c < n; c += 1) {
3892
- let best = Number.POSITIVE_INFINITY;
3893
- let bestPrev = 0;
3894
- for (let p = 0; p < n; p += 1) {
3895
- if (articulates && sameHarmony(candAt(p), candAt(c))) {
3896
- continue;
3897
- }
3898
- const cost = (_nullishCoalesce(dp[p], () => ( Number.POSITIVE_INFINITY))) + transitionCost(candAt(p), candAt(c), tonicPc) * seg.beats + (closes ? phraseEndBonus(candAt(p), candAt(c), tonicPc, seg, melody) : 0);
3899
- if (cost < best) {
3900
- best = cost;
3901
- bestPrev = p;
3902
- }
3903
- }
3904
- next[c] = best + emissionCost(seg, candAt(c), melody, key) + (_nullishCoalesce(jitter[c], () => ( 0)));
3905
- ptr[c] = bestPrev;
3906
- }
3907
- dp = next;
3908
- back.push(ptr);
3909
- }
3910
- let bestCost = Number.POSITIVE_INFINITY;
3911
- let bestEnd = 0;
3912
- for (let c = 0; c < n; c += 1) {
3913
- const total = _nullishCoalesce(dp[c], () => ( Number.POSITIVE_INFINITY));
3914
- if (total < bestCost) {
3915
- bestCost = total;
3916
- bestEnd = c;
3917
- }
3918
- }
3919
- const path = new Array(segments.length).fill(bestEnd);
3920
- for (let s = segments.length - 2; s >= 0; s -= 1) {
3921
- const ptr = back[s];
3922
- const nextIdx = _nullishCoalesce(path[s + 1], () => ( bestEnd));
3923
- path[s] = ptr ? _nullishCoalesce(ptr[nextIdx], () => ( bestEnd)) : bestEnd;
3924
- }
3925
- return { cost: bestCost, path };
3926
- }
3927
- function harmonizeMelody(opts) {
3928
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, opts.melody, "harmonize melody", { allowNonPositiveDuration: true });
3929
- const phraseEnds = _nullishCoalesce(opts.phraseEnds, () => ( []));
3930
- for (const end of phraseEnds) {
3931
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, end, "harmonize phrase end");
3932
- }
3933
- const soundingMelody = _chunkQ2WGKJNNcjs.soundingNotesOnly.call(void 0, opts.melody);
3934
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_METER));
3935
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
3936
- const requestedKey = _nullishCoalesce(opts.key, () => ( "infer"));
3937
- const key = requestedKey === "infer" ? inferKey(soundingMelody) : _chunkUADYAYATcjs.toKeyScale.call(void 0, requestedKey);
3938
- const placement = _nullishCoalesce(opts.placement, () => ( DEFAULT_PLACEMENT));
3939
- const ctx = resolveContext(opts.ctx);
3940
- const harmonic = _nullishCoalesce(ctx.harmonic, () => ( REHARMONIZE_DIAL[_nullishCoalesce(opts.reharmonize, () => ( "diatonic"))]));
3941
- if (soundingMelody.length === 0) {
3942
- return { transposeSemitones: 0, key, chords: [], melodyRoles: [] };
3943
- }
3944
- const candidates = buildCandidates(key, harmonic);
3945
- const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
3946
- const draw = ctx.part("harmony");
3947
- const jitter = candidates.map((_, index) => draw.at("jitter", index) * TIE_BREAK_JITTER2);
3948
- const pulse2 = _chunkK4ZJUGF4cjs.pulseBeats.call(void 0, ts);
3949
- const spans = soundingMelody.map((note2) => ({
3950
- startBeat: snapToPulse(note2.startBeat, pulse2),
3951
- endBeat: snapToPulse(note2.startBeat + note2.durationBeat, pulse2)
3952
- }));
3953
- const melodyStart = spans.reduce(
3954
- (start, span) => Math.min(start, span.startBeat),
3955
- Number.POSITIVE_INFINITY
3956
- );
3957
- const melodyEnd = spans.reduce((end, span) => Math.max(end, span.endBeat), 0);
3958
- const hr = _chunkQ2WGKJNNcjs.assertRange.call(void 0,
3959
- _nullishCoalesce(opts.harmonicRhythm, () => ( defaultHarmonicRhythm(ts))),
3960
- Number.MIN_VALUE,
3961
- Number.MAX_SAFE_INTEGER,
3962
- "harmonic rhythm"
3963
- );
4241
+ function buildSegments(spans, melody, ts, grid, phraseEnds, budget) {
4242
+ const melodyStart = grid.start;
4243
+ const melodyEnd = grid.end;
4244
+ const hr = grid.harmonicRhythm;
3964
4245
  const segmentStart = Math.floor(melodyStart / hr) * hr;
3965
4246
  const gridCount = Math.max(1, Math.ceil((melodyEnd - segmentStart) / hr));
3966
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, gridCount, "harmonic segments");
3967
- const bounds = gridBounds(segmentStart, hr, gridCount, phraseEnds);
4247
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, gridCount, "harmonic segments", budget);
4248
+ const innerEnds = phraseEnds.filter(
4249
+ (end) => end > segmentStart + _chunkS5BKZZGMcjs.BEAT_EPS && end < melodyEnd - _chunkS5BKZZGMcjs.BEAT_EPS
4250
+ );
4251
+ const bounds = gridBounds(segmentStart, hr, gridCount, innerEnds);
3968
4252
  const segCount = bounds.length - 1;
3969
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, segCount, "harmonic segments");
4253
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, segCount, "harmonic segments", budget);
3970
4254
  const segments = Array.from({ length: segCount }, (_, s) => {
3971
4255
  const startBeat = _nullishCoalesce(bounds[s], () => ( segmentStart));
3972
4256
  const endBeat = _nullishCoalesce(bounds[s + 1], () => ( startBeat + hr));
@@ -3987,16 +4271,16 @@ function harmonizeMelody(opts) {
3987
4271
  lastExclusive += 1;
3988
4272
  }
3989
4273
  memberships += Math.max(0, lastExclusive - first);
3990
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, memberships, "note-to-segment memberships");
4274
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, memberships, "note-to-segment memberships", budget);
3991
4275
  for (let segment = first; segment < lastExclusive; segment += 1) {
3992
- _optionalChain([segments, 'access', _46 => _46[segment], 'optionalAccess', _47 => _47.noteIndices, 'access', _48 => _48.push, 'call', _49 => _49(index)]);
4276
+ _optionalChain([segments, 'access', _50 => _50[segment], 'optionalAccess', _51 => _51.noteIndices, 'access', _52 => _52.push, 'call', _53 => _53(index)]);
3993
4277
  }
3994
4278
  }
3995
- const tones = classifyMelodyTones(soundingMelody, ts);
3996
- const phraseClosingNotes = closingNoteIndices(spans, phraseEnds);
4279
+ const tones = classifyMelodyTones(melody, ts);
4280
+ const phraseClosingNotes = closingNoteIndices(spans, innerEnds);
3997
4281
  for (const segment of segments) {
3998
4282
  const structural = segment.noteIndices.filter(
3999
- (idx) => _optionalChain([tones, 'access', _50 => _50[idx], 'optionalAccess', _51 => _51.ornamental]) !== true || phraseClosingNotes.has(idx)
4283
+ (idx) => _optionalChain([tones, 'access', _54 => _54[idx], 'optionalAccess', _55 => _55.ornamental]) !== true || phraseClosingNotes.has(idx)
4000
4284
  );
4001
4285
  const costIndices = structural.length > 0 ? structural : segment.noteIndices;
4002
4286
  let closingStart = Number.NEGATIVE_INFINITY;
@@ -4011,7 +4295,7 @@ function harmonizeMelody(opts) {
4011
4295
  continue;
4012
4296
  }
4013
4297
  const weight = Math.max(0.25, overlap);
4014
- segment.costNotes.push({ index, weight, strong: _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, overlapStart, ts) });
4298
+ segment.costNotes.push({ index, weight, strong: _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, overlapStart, ts) });
4015
4299
  segment.weight += weight;
4016
4300
  if (span.startBeat >= closingStart) {
4017
4301
  closingStart = span.startBeat;
@@ -4019,9 +4303,200 @@ function harmonizeMelody(opts) {
4019
4303
  }
4020
4304
  }
4021
4305
  }
4306
+ return { segments, innerEnds, bounds, segmentStart };
4307
+ }
4308
+
4309
+ // src/generate/harmonize/candidates.ts
4310
+ var ROOT_WEIGHT_BY_SEMITONE = [0, 0.9, 0.3, 0.45, 0.45, 0.15, 0.9, 0.1, 0.3, 0.3, 0.3, 0.9];
4311
+ var UNSTABLE_TRIAD = 0.9;
4312
+ var UNSTABLE_QUALITIES = /* @__PURE__ */ new Set([
4313
+ "dim",
4314
+ "aug",
4315
+ "dim7",
4316
+ "m7b5",
4317
+ "aug7"
4318
+ ]);
4319
+ function vocabularyBase(rootPc, quality, tonicPc) {
4320
+ const above = (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc) - tonicPc + 12) % 12;
4321
+ const root = _nullishCoalesce(ROOT_WEIGHT_BY_SEMITONE[above], () => ( UNSTABLE_TRIAD));
4322
+ return UNSTABLE_QUALITIES.has(quality) ? Math.max(root, UNSTABLE_TRIAD) : root;
4323
+ }
4324
+ var SECONDARY_DOMINANT_BASE = 0.5;
4325
+ var BORROWED_BASE = 0.5;
4326
+ var SECONDARY_DOMINANT_TARGETS = [2, 4, 5, 6];
4327
+ function secondaryDominantTargets(key) {
4328
+ const tonicizable = new Map(
4329
+ _chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key).map((degree) => [degree.degreeNumber, degree])
4330
+ );
4331
+ return SECONDARY_DOMINANT_TARGETS.map((degree) => tonicizable.get(degree)).filter(
4332
+ (degree) => degree !== void 0
4333
+ );
4334
+ }
4335
+ var SECONDARY_DOMINANT_ENTRY_ORDER = [5, 6, 2, 4];
4336
+ var BORROWED_ENTRY_ORDER = [5, 10, 8, 3, 7, 2, 0];
4337
+ var SECONDARY_DOMINANT_BAND = 0.5;
4338
+ var REHARMONIZE_STRENGTHS = ["diatonic", "secondaryDominant", "borrowed"];
4339
+ var REHARMONIZE_DIAL = {
4340
+ diatonic: 0,
4341
+ secondaryDominant: SECONDARY_DOMINANT_BAND,
4342
+ borrowed: 1
4343
+ };
4344
+ function admittedCount(fraction, size) {
4345
+ return Math.max(0, Math.min(size, Math.ceil(fraction * size)));
4346
+ }
4347
+ function buildCandidates(key, harmonic) {
4348
+ const tonicPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
4349
+ const frame = _chunk3R3QVKY5cjs.heptatonicFrameOf.call(void 0, key);
4350
+ const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, frame);
4351
+ const candidates = tones.map((rootPc, index) => {
4352
+ const degree = index + 1;
4353
+ const quality = _chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, frame).quality;
4354
+ return {
4355
+ rootPc,
4356
+ quality,
4357
+ degree,
4358
+ secondaryDominant: false,
4359
+ base: vocabularyBase(rootPc, quality, tonicPc),
4360
+ pcs: _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, quality))
4361
+ };
4362
+ });
4363
+ if (cadencesThroughRaisedSeventh(key)) {
4364
+ const rootPc = (tonicPc + 7) % 12;
4365
+ candidates.push({
4366
+ rootPc,
4367
+ quality: "maj",
4368
+ degree: 5,
4369
+ secondaryDominant: false,
4370
+ base: vocabularyBase(rootPc, "maj", tonicPc),
4371
+ pcs: _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, "maj"))
4372
+ });
4373
+ }
4374
+ const targets = secondaryDominantTargets(key);
4375
+ const entryOrder = SECONDARY_DOMINANT_ENTRY_ORDER.filter(
4376
+ (degree) => targets.some((target) => target.degreeNumber === degree)
4377
+ );
4378
+ const secondaryOpen = admittedCount(harmonic / SECONDARY_DOMINANT_BAND, targets.length);
4379
+ for (const target of targets) {
4380
+ if (entryOrder.indexOf(target.degreeNumber) >= secondaryOpen) {
4381
+ continue;
4382
+ }
4383
+ const rootPc = (target.rootPc + 7) % 12;
4384
+ candidates.push({
4385
+ rootPc,
4386
+ quality: "dom7",
4387
+ secondaryDominant: true,
4388
+ targetDegree: target.degreeNumber,
4389
+ base: SECONDARY_DOMINANT_BASE,
4390
+ pcs: _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, "dom7"))
4391
+ });
4392
+ }
4393
+ const parallel = _chunkSGLEQVJHcjs.parallelKey.call(void 0, key);
4394
+ const parallelTones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, parallel);
4395
+ const borrowed = [];
4396
+ for (let degree = 1; degree <= parallelTones.length; degree += 1) {
4397
+ const chord = _chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, parallel);
4398
+ if (!_chunkSGLEQVJHcjs.isDiatonic.call(void 0, chord, key)) {
4399
+ borrowed.push(chord);
4400
+ }
4401
+ }
4402
+ const borrowedRank = (chord) => {
4403
+ const offset = BORROWED_ENTRY_ORDER.indexOf(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - tonicPc));
4404
+ return offset < 0 ? BORROWED_ENTRY_ORDER.length : offset;
4405
+ };
4406
+ borrowed.sort((a, b) => borrowedRank(a) - borrowedRank(b));
4407
+ const borrowedOpen = admittedCount(
4408
+ (harmonic - SECONDARY_DOMINANT_BAND) / (1 - SECONDARY_DOMINANT_BAND),
4409
+ borrowed.length
4410
+ );
4411
+ for (const chord of borrowed.slice(0, borrowedOpen)) {
4412
+ candidates.push({
4413
+ rootPc: chord.rootPc,
4414
+ quality: chord.quality,
4415
+ secondaryDominant: false,
4416
+ // A diminished or augmented triad is charged for what it is wherever it
4417
+ // comes from: priced at the flat borrowing rate, the ii dim borrowed from
4418
+ // the parallel minor came out cheaper than the key's own vii dim, and
4419
+ // which of the two a phrase took was decided by the tie-break jitter
4420
+ // rather than by the melody.
4421
+ base: UNSTABLE_QUALITIES.has(chord.quality) ? Math.max(BORROWED_BASE, UNSTABLE_TRIAD) : BORROWED_BASE,
4422
+ pcs: _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord)
4423
+ });
4424
+ }
4425
+ const seen = /* @__PURE__ */ new Set();
4426
+ return candidates.filter((candidate) => {
4427
+ const id = `${candidate.rootPc}:${candidate.quality}`;
4428
+ if (seen.has(id)) {
4429
+ return false;
4430
+ }
4431
+ seen.add(id);
4432
+ return true;
4433
+ });
4434
+ }
4435
+
4436
+ // src/generate/harmonize/index.ts
4437
+ function harmonizeMelody(opts) {
4438
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "harmonize options");
4439
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, opts.melody, "harmonize melody", {
4440
+ allowNonPositiveDuration: true,
4441
+ budget: opts.budget
4442
+ });
4443
+ const phraseEnds = _nullishCoalesce(opts.phraseEnds, () => ( []));
4444
+ for (const end of phraseEnds) {
4445
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, end, "harmonize phrase end");
4446
+ }
4447
+ const soundingMelody = _chunkS5BKZZGMcjs.soundingNotesOnly.call(void 0, opts.melody);
4448
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_METER)), "ts"));
4449
+ _chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, ts);
4450
+ const requestedKey = _nullishCoalesce(opts.key, () => ( "infer"));
4451
+ const resolvedKey = _chunk3R3QVKY5cjs.resolveKey.call(void 0,
4452
+ requestedKey === "infer" ? inferKey(soundingMelody) : requestedKey
4453
+ );
4454
+ const key = resolvedKey.scale;
4455
+ const placement = _nullishCoalesce(opts.placement, () => ( DEFAULT_PLACEMENT));
4456
+ const ctx = resolveContext(opts.ctx);
4457
+ const reharmonize = _chunkS5BKZZGMcjs.assertOneOf.call(void 0,
4458
+ _nullishCoalesce(opts.reharmonize, () => ( "diatonic")),
4459
+ REHARMONIZE_STRENGTHS,
4460
+ "reharmonize"
4461
+ );
4462
+ const harmonic = _nullishCoalesce(ctx.harmonic, () => ( REHARMONIZE_DIAL[reharmonize]));
4463
+ if (soundingMelody.length === 0) {
4464
+ return { transposeSemitones: 0, key: resolvedKey, chords: [], melodyRoles: [] };
4465
+ }
4466
+ const candidates = buildCandidates(key, harmonic);
4467
+ const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
4468
+ const draw = ctx.part("harmony");
4469
+ const jitter = candidates.map((_, index) => draw.at("jitter", index) * TIE_BREAK_JITTER2);
4470
+ const pulse2 = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
4471
+ const spans = soundingMelody.map((note2) => ({
4472
+ startBeat: snapToPulse(note2.startBeat, pulse2),
4473
+ endBeat: snapToPulse(note2.startBeat + note2.durationBeat, pulse2)
4474
+ }));
4475
+ const melodyStart = spans.reduce(
4476
+ (start, span) => Math.min(start, span.startBeat),
4477
+ Number.POSITIVE_INFINITY
4478
+ );
4479
+ const melodyEnd = spans.reduce(
4480
+ (end, span) => Math.max(end, span.endBeat),
4481
+ Number.NEGATIVE_INFINITY
4482
+ );
4483
+ const hr = _chunkS5BKZZGMcjs.assertRange.call(void 0,
4484
+ _nullishCoalesce(opts.harmonicRhythm, () => ( defaultHarmonicRhythm(ts))),
4485
+ Number.MIN_VALUE,
4486
+ Number.MAX_SAFE_INTEGER,
4487
+ "harmonic rhythm"
4488
+ );
4489
+ const { segments, innerEnds, bounds, segmentStart } = buildSegments(
4490
+ spans,
4491
+ soundingMelody,
4492
+ ts,
4493
+ { start: melodyStart, end: melodyEnd, harmonicRhythm: hr },
4494
+ phraseEnds,
4495
+ opts.budget
4496
+ );
4022
4497
  const { closing: closingSegments, named: namedEndSegments } = phraseEndSegments(
4023
4498
  segments,
4024
- phraseEnds
4499
+ innerEnds
4025
4500
  );
4026
4501
  const articulatedSegments = new Set(candidates.map((c) => `${c.rootPc}:${c.quality}`)).size > 1 ? namedEndSegments : /* @__PURE__ */ new Set();
4027
4502
  const semitoneShifts = [0];
@@ -4034,9 +4509,10 @@ function harmonizeMelody(opts) {
4034
4509
  }
4035
4510
  const octaveShifts = placement.octaveSearch ? [0, -12, 12] : [0];
4036
4511
  const transposes = semitoneShifts.flatMap((s) => octaveShifts.map((o) => s + o));
4037
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0,
4038
- transposes.length * segCount * candidates.length * candidates.length,
4039
- "harmonization placement search"
4512
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
4513
+ transposes.length * segments.length * candidates.length * candidates.length,
4514
+ "harmonization placement search",
4515
+ opts.budget
4040
4516
  );
4041
4517
  let bestCost = Number.POSITIVE_INFINITY;
4042
4518
  let bestTs = 0;
@@ -4065,7 +4541,7 @@ function harmonizeMelody(opts) {
4065
4541
  const chord = {
4066
4542
  rootPc: cand.rootPc,
4067
4543
  quality: cand.quality,
4068
- startBeat: _nullishCoalesce(_optionalChain([segments, 'access', _52 => _52[s], 'optionalAccess', _53 => _53.startBeat]), () => ( segmentStart + s * hr))
4544
+ startBeat: _nullishCoalesce(_optionalChain([segments, 'access', _56 => _56[s], 'optionalAccess', _57 => _57.startBeat]), () => ( segmentStart + s * hr))
4069
4545
  };
4070
4546
  if (cand.degree !== void 0) {
4071
4547
  chord.degree = cand.degree;
@@ -4077,29 +4553,37 @@ function harmonizeMelody(opts) {
4077
4553
  })
4078
4554
  );
4079
4555
  const melodyRoles = opts.melody.map((note2, noteIndex) => {
4080
- const segIdx = segmentIndexAt(bounds, note2.startBeat);
4556
+ const segIdx = segmentIndexAt(bounds, snapToPulse(note2.startBeat, pulse2));
4081
4557
  const cand = candAt(_nullishCoalesce(bestPath[segIdx], () => ( 0)));
4082
- const chord = _chunkUADYAYATcjs.makeChord.call(void 0, cand.rootPc, cand.quality);
4083
- return { noteIndex, role: _chunk6XPTBE7Ycjs.roleOf.call(void 0, note2.pitch + bestTs, chord).role };
4558
+ const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, cand.rootPc, cand.quality);
4559
+ return { noteIndex, role: _chunkPM4XJE2Ycjs.roleOf.call(void 0, note2.pitch + bestTs, chord).role };
4084
4560
  });
4085
- return { transposeSemitones: bestTs, key, chords, melodyRoles };
4561
+ return { transposeSemitones: bestTs, key: resolvedKey, chords, melodyRoles };
4086
4562
  }
4087
4563
 
4088
4564
  // src/generate/motif/index.ts
4565
+ var MOTIF_TRANSFORMS = Object.freeze([
4566
+ "transposeDiatonic",
4567
+ "transposeChromatic",
4568
+ "invert",
4569
+ "retrograde",
4570
+ "augment",
4571
+ "diminish",
4572
+ "sequence"
4573
+ ]);
4089
4574
  var MIN_TILE_SPAN = 1 / 256;
4090
- var DEFAULT_TS5 = { numerator: 4, denominator: 4 };
4091
- var EPS4 = 1e-9;
4575
+ var DEFAULT_TS4 = { numerator: 4, denominator: 4 };
4092
4576
  function isDownbeat(startBeat, barBeats) {
4093
4577
  const bar2 = startBeat / barBeats;
4094
- return Math.abs(bar2 - Math.round(bar2)) < EPS4;
4578
+ return Math.abs(bar2 - Math.round(bar2)) < _chunkS5BKZZGMcjs.BEAT_EPS;
4095
4579
  }
4096
4580
  function nearestChordTone(pitch, chord) {
4097
- const pcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, chord);
4581
+ const pcs = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord);
4098
4582
  for (let d = 0; d <= 12; d += 1) {
4099
- if (pcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pitch - d))) {
4583
+ if (pcs.includes(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch - d))) {
4100
4584
  return pitch - d;
4101
4585
  }
4102
- if (pcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pitch + d))) {
4586
+ if (pcs.includes(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch + d))) {
4103
4587
  return pitch + d;
4104
4588
  }
4105
4589
  }
@@ -4146,6 +4630,18 @@ function cellSpan(cell) {
4146
4630
  function clone(cell) {
4147
4631
  return { notes: cell.notes.map((n) => ({ ...n })) };
4148
4632
  }
4633
+ function inTimeOrder(cell) {
4634
+ return { notes: [...cell.notes].sort((a, b) => a.startBeat - b.startBeat) };
4635
+ }
4636
+ function pivotPitch(notes) {
4637
+ let pivot;
4638
+ for (const note2 of notes) {
4639
+ if (pivot === void 0 || note2.startBeat < pivot.startBeat) {
4640
+ pivot = note2;
4641
+ }
4642
+ }
4643
+ return _nullishCoalesce(_optionalChain([pivot, 'optionalAccess', _58 => _58.pitch]), () => ( 0));
4644
+ }
4149
4645
  function contourOffsets(contour, count) {
4150
4646
  const offsets = [];
4151
4647
  for (let i = 0; i < count; i += 1) {
@@ -4167,19 +4663,20 @@ function contourOffsets(contour, count) {
4167
4663
  return offsets;
4168
4664
  }
4169
4665
  function generateMotif(opts) {
4666
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "motif options");
4170
4667
  const contour = _nullishCoalesce(opts.contour, () => ( "arch"));
4171
- const bars = _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, opts.bars, "motif bars");
4172
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS5)));
4668
+ const bars = _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, opts.bars, "motif bars");
4669
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS4)), "ts")));
4173
4670
  const totalBeats = bars * barBeats;
4174
4671
  const noteCount = Math.max(3, bars * 2);
4175
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, noteCount, "motif notes");
4672
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, noteCount, "motif notes");
4176
4673
  const beatsPerNote = totalBeats / noteCount;
4177
4674
  const ctx = resolveContextWith(opts.ctx, { ornament: opts.jitter });
4178
4675
  const jitterProb = _nullishCoalesce(ctx.ornament, () => ( 0));
4179
4676
  const draw = ctx.part("motif");
4180
- const key = _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key);
4181
- const chord = opts.chord ? _chunkUADYAYATcjs.toChordData.call(void 0, opts.chord) : null;
4182
- const tonic = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc) + 60;
4677
+ const key = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, opts.key);
4678
+ const chord = opts.chord ? _chunk3R3QVKY5cjs.toChordData.call(void 0, opts.chord) : null;
4679
+ const tonic = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc) + 60;
4183
4680
  const offsets = contourOffsets(contour, noteCount);
4184
4681
  const notes = [];
4185
4682
  for (let i = 0; i < noteCount; i += 1) {
@@ -4187,13 +4684,13 @@ function generateMotif(opts) {
4187
4684
  if (draw.prob(jitterProb, "jitter", i)) {
4188
4685
  jitter = draw.prob(0.5, "direction", i) ? 1 : -1;
4189
4686
  }
4190
- let pitch = _chunkUADYAYATcjs.shiftByScaleDegrees.call(void 0, tonic, (_nullishCoalesce(offsets[i], () => ( 0))) + jitter, key);
4687
+ let pitch = _chunk3R3QVKY5cjs.shiftByScaleDegrees.call(void 0, tonic, (_nullishCoalesce(offsets[i], () => ( 0))) + jitter, key);
4191
4688
  const startBeat = i * beatsPerNote;
4192
4689
  if (chord && isDownbeat(startBeat, barBeats)) {
4193
4690
  pitch = nearestChordTone(pitch, chord);
4194
4691
  }
4195
4692
  notes.push({
4196
- pitch: _chunkQ2WGKJNNcjs.clampToMidi.call(void 0, pitch, "generated motif pitch"),
4693
+ pitch: _chunkS5BKZZGMcjs.clampToMidi.call(void 0, pitch, "generated motif pitch"),
4197
4694
  startBeat,
4198
4695
  durationBeat: beatsPerNote
4199
4696
  });
@@ -4201,18 +4698,23 @@ function generateMotif(opts) {
4201
4698
  return { notes };
4202
4699
  }
4203
4700
  function motifToNoteEvents(cell) {
4204
- return cell.notes.map((note2) => ({ ...note2 }));
4701
+ return assertCellNotes(cell, "motif cell notes").map((note2) => ({ ...note2 }));
4702
+ }
4703
+ function assertCellNotes(cell, name) {
4704
+ const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, cell, "motif cell");
4705
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, read.notes, name);
4706
+ return read.notes;
4205
4707
  }
4206
4708
  function transformMotif(cell, t, amount, key) {
4207
- return transformCell(cell, t, amount, key === void 0 ? void 0 : _chunkUADYAYATcjs.toKeyScale.call(void 0, key));
4709
+ return transformCell(cell, t, amount, key === void 0 ? void 0 : _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
4208
4710
  }
4209
4711
  function transformCell(cell, t, amount, key) {
4210
- return assertInRange(transformUnchecked(cell, t, amount, key));
4712
+ return assertInRange(inTimeOrder(transformUnchecked(cell, t, amount, key)));
4211
4713
  }
4212
4714
  function assertInRange(cell) {
4213
4715
  for (const note2 of cell.notes) {
4214
4716
  if (note2.pitch < 0 || note2.pitch > 127) {
4215
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
4717
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4216
4718
  `transformed motif pitch ${note2.pitch} is outside the MIDI range 0..127`
4217
4719
  );
4218
4720
  }
@@ -4220,20 +4722,20 @@ function assertInRange(cell) {
4220
4722
  return cell;
4221
4723
  }
4222
4724
  function transformUnchecked(cell, t, amount, key) {
4223
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
4725
+ const notes = assertCellNotes(cell, "motif notes");
4726
+ _chunkS5BKZZGMcjs.assertOneOf.call(void 0, t, MOTIF_TRANSFORMS, "motif transform");
4224
4727
  if (amount !== void 0) {
4225
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, amount, "motif transform amount");
4728
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, amount, "motif transform amount");
4226
4729
  }
4227
4730
  if ((t === "transposeChromatic" || t === "transposeDiatonic" || t === "sequence") && amount !== void 0) {
4228
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0,
4731
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0,
4229
4732
  amount,
4230
4733
  `${t === "transposeChromatic" ? "chromatic" : "diatonic"} transform amount`
4231
4734
  );
4232
4735
  }
4233
4736
  if ((t === "augment" || t === "diminish") && amount !== void 0 && amount <= 0) {
4234
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("time transform amount must be positive");
4737
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("time transform amount must be positive");
4235
4738
  }
4236
- const notes = cell.notes;
4237
4739
  switch (t) {
4238
4740
  case "transposeChromatic": {
4239
4741
  const semis = _nullishCoalesce(amount, () => ( 0));
@@ -4250,12 +4752,12 @@ function transformUnchecked(cell, t, amount, key) {
4250
4752
  // notes a motif is written with — a blues lick, a semitone neighbour —
4251
4753
  // come through as themselves, and shifting by no degrees returns the
4252
4754
  // cell it was given.
4253
- pitch: key ? _chunkUADYAYATcjs.shiftByScaleDegrees.call(void 0, n.pitch, degrees, key) : n.pitch + degrees
4755
+ pitch: key ? _chunk3R3QVKY5cjs.shiftByScaleDegrees.call(void 0, n.pitch, degrees, key) : n.pitch + degrees
4254
4756
  }))
4255
4757
  };
4256
4758
  }
4257
4759
  case "invert": {
4258
- const pivot = _nullishCoalesce(_optionalChain([notes, 'access', _54 => _54[0], 'optionalAccess', _55 => _55.pitch]), () => ( 0));
4760
+ const pivot = pivotPitch(notes);
4259
4761
  return { notes: notes.map((n) => ({ ...n, pitch: 2 * pivot - n.pitch })) };
4260
4762
  }
4261
4763
  case "retrograde": {
@@ -4295,34 +4797,36 @@ function scaleTime(cell, factor) {
4295
4797
  }))
4296
4798
  };
4297
4799
  }
4298
- function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS5) {
4299
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, bars, "development bars");
4300
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
4301
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
4800
+ function developMotif(cell, timeline, key, bars, ts) {
4801
+ const meter = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
4802
+ _chunkSGLEQVJHcjs.assertChordTimeline.call(void 0, timeline);
4803
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, bars, "development bars");
4804
+ const cellNotes = assertCellNotes(cell, "motif notes");
4805
+ const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
4302
4806
  const span = cellSpan(cell);
4303
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
4807
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, meter);
4304
4808
  const totalBeats = bars * barBeats;
4305
- const origin = cellOrigin(cell.notes);
4809
+ const origin = cellOrigin(cellNotes);
4306
4810
  const out = [];
4307
4811
  if (span <= 0) {
4308
- return clone(cell);
4812
+ return inTimeOrder(cell);
4309
4813
  }
4310
4814
  const tileSpan = Math.max(span, MIN_TILE_SPAN);
4311
4815
  const tileCount = Math.ceil(totalBeats / tileSpan);
4312
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, tileCount * cell.notes.length, "developed motif notes");
4816
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, tileCount * cellNotes.length, "developed motif notes");
4313
4817
  for (let k = 0; k < tileCount; k += 1) {
4314
4818
  const offset = k * tileSpan;
4315
4819
  const taken = /* @__PURE__ */ new Map();
4316
4820
  let previous;
4317
- for (const n of cell.notes) {
4821
+ for (const n of cellNotes) {
4318
4822
  const startBeat = offset + (n.startBeat - origin);
4319
4823
  if (startBeat >= totalBeats) {
4320
4824
  continue;
4321
4825
  }
4322
4826
  const chord = timeline.at(startBeat);
4323
4827
  const structural = n.startBeat === origin || isDownbeat(startBeat, barBeats);
4324
- const chordTones = chord && structural ? _chunkUADYAYATcjs.chordPitchClasses.call(void 0, chord) : null;
4325
- const allows = chordTones ? (candidate) => chordTones.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, candidate)) : (candidate) => _chunkUADYAYATcjs.isScaleTone.call(void 0, candidate, scale);
4828
+ const chordTones = chord && structural ? _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord) : null;
4829
+ const allows = chordTones ? (candidate) => chordTones.includes(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, candidate)) : (candidate) => _chunk3R3QVKY5cjs.isScaleTone.call(void 0, candidate, scale);
4326
4830
  const direction = previous === void 0 ? 0 : n.pitch - previous;
4327
4831
  const pitch = developedPitch(n.pitch, allows, direction, taken);
4328
4832
  taken.set(pitch, n.pitch);
@@ -4330,12 +4834,12 @@ function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS5) {
4330
4834
  out.push({ pitch, startBeat, durationBeat: n.durationBeat });
4331
4835
  }
4332
4836
  }
4333
- return { notes: out };
4837
+ return inTimeOrder({ notes: out });
4334
4838
  }
4335
4839
 
4336
4840
  // src/generate/ornament/index.ts
4337
4841
  var ORNAMENT_STYLES = Object.freeze(["ghost", "flam", "drag", "slide", "accent"]);
4338
- var DEFAULT_TS6 = { numerator: 4, denominator: 4 };
4842
+ var DEFAULT_TS5 = { numerator: 4, denominator: 4 };
4339
4843
  var DEFAULT_STYLE2 = "ghost";
4340
4844
  var DEFAULT_AMOUNT = 0.5;
4341
4845
  var GHOST_VELOCITY_SCALE = 0.45;
@@ -4343,9 +4847,9 @@ var ACCENT_VELOCITY_BOOST = 12;
4343
4847
  var DEFAULT_VELOCITY2 = 80;
4344
4848
  var MIN_VELOCITY2 = 1;
4345
4849
  var MAX_VELOCITY2 = 127;
4346
- var SLIDE_MIN_SEMITONES = 2;
4850
+ var SLIDE_MIN_SEMITONES = 1;
4347
4851
  function eligible(style, note2, previous, next, ts) {
4348
- const strong = _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, note2.startBeat, ts);
4852
+ const strong = _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, note2.startBeat, ts);
4349
4853
  switch (style) {
4350
4854
  case "ghost":
4351
4855
  return !strong;
@@ -4353,7 +4857,7 @@ function eligible(style, note2, previous, next, ts) {
4353
4857
  case "accent":
4354
4858
  return strong;
4355
4859
  case "drag":
4356
- return !strong && next !== void 0 && _chunkK4ZJUGF4cjs.isStrongBeat.call(void 0, next.startBeat, ts);
4860
+ return !strong && next !== void 0 && _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, next.startBeat, ts);
4357
4861
  case "slide":
4358
4862
  return previous !== void 0 && Math.abs(note2.pitch - previous.pitch) >= SLIDE_MIN_SEMITONES;
4359
4863
  }
@@ -4378,28 +4882,23 @@ function clampVelocity(velocity) {
4378
4882
  return Math.min(MAX_VELOCITY2, Math.max(MIN_VELOCITY2, Math.round(velocity)));
4379
4883
  }
4380
4884
  function ornament(notes, opts = {}) {
4381
- _chunkQ2WGKJNNcjs.assertNoteEvents.call(void 0, notes, "ornament notes", { allowNonPositiveDuration: true });
4382
- const style = _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.style, () => ( DEFAULT_STYLE2)), ORNAMENT_STYLES, "ornament style");
4383
- const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS6));
4384
- _chunkQ2WGKJNNcjs.assertTimeSignature.call(void 0, ts);
4385
- if (opts.amount !== void 0) {
4386
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, opts.amount, 0, 1, "ornament amount");
4387
- }
4388
- const ctx = resolveContextWith(opts.ctx, { ornament: opts.amount });
4885
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
4886
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "ornament notes", { allowNonPositiveDuration: true });
4887
+ const style = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.style, () => ( DEFAULT_STYLE2)), ORNAMENT_STYLES, "ornament style");
4888
+ const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, _nullishCoalesce(asked.ts, () => ( DEFAULT_TS5)), "ts"));
4889
+ _chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, ts);
4890
+ if (asked.amount !== void 0) {
4891
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, asked.amount, 0, 1, "ornament amount");
4892
+ }
4893
+ const ctx = resolveContextWith(asked.ctx, { ornament: asked.amount });
4389
4894
  const amount = _nullishCoalesce(ctx.ornament, () => ( DEFAULT_AMOUNT));
4390
4895
  const draw = ctx.part("ornament");
4391
- const sounding = _chunkQ2WGKJNNcjs.dropSilentNotes.call(void 0, notes);
4392
- const ordered = [...sounding].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
4393
- const previousOf = /* @__PURE__ */ new Map();
4394
- for (let index = 0; index < ordered.length; index += 1) {
4395
- const note2 = ordered[index];
4396
- if (note2) {
4397
- previousOf.set(note2, index === 0 ? void 0 : ordered[index - 1]);
4398
- }
4399
- }
4896
+ const sounding2 = _chunkS5BKZZGMcjs.dropSilentNotes.call(void 0, notes);
4897
+ const ordered = [...sounding2].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
4898
+ const previousOf = previousOnsets(ordered);
4400
4899
  const nextOf = nextOnsets(ordered);
4401
4900
  const gaps = strokeGaps(ordered);
4402
- return sounding.map((note2) => {
4901
+ return sounding2.map((note2) => {
4403
4902
  const decorated = { ...note2 };
4404
4903
  if (note2.articulation !== void 0 || !eligible(style, note2, previousOf.get(note2), nextOf.get(note2), ts)) {
4405
4904
  return decorated;
@@ -4418,6 +4917,19 @@ function ornament(notes, opts = {}) {
4418
4917
  return decorated;
4419
4918
  });
4420
4919
  }
4920
+ function previousOnsets(ordered) {
4921
+ const previousOf = /* @__PURE__ */ new Map();
4922
+ let earlier;
4923
+ for (const note2 of ordered) {
4924
+ if (earlier !== void 0 && earlier.startBeat === note2.startBeat) {
4925
+ previousOf.set(note2, previousOf.get(earlier));
4926
+ } else {
4927
+ previousOf.set(note2, earlier);
4928
+ }
4929
+ earlier = note2;
4930
+ }
4931
+ return previousOf;
4932
+ }
4421
4933
  function nextOnsets(ordered) {
4422
4934
  const nextOf = /* @__PURE__ */ new Map();
4423
4935
  let later;
@@ -4450,6 +4962,8 @@ function strokeGaps(ordered) {
4450
4962
  }
4451
4963
 
4452
4964
  // src/generate/progression/index.ts
4965
+ var DEFAULT_TS6 = { numerator: 4, denominator: 4 };
4966
+ var PROG_STYLES = Object.freeze(["minimal", "dance", "idol", "rock"]);
4453
4967
  var BORROWED_DEGREES = Object.freeze({
4454
4968
  /** Flat submediant: bVI. */
4455
4969
  bVI: 8,
@@ -4633,23 +5147,23 @@ function progressions() {
4633
5147
  return PRESETS.map((p) => ({ ...p, degrees: [...p.degrees], styles: [...p.styles] }));
4634
5148
  }
4635
5149
  function progressionsByStyle(style) {
4636
- return progressions().filter((p) => p.styles.includes(style));
5150
+ const named = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, style, PROG_STYLES, "style");
5151
+ return progressions().filter((p) => p.styles.includes(named));
4637
5152
  }
4638
5153
  function degreeToRootPc(degree, key) {
4639
5154
  if (degree >= 1 && degree <= 7) {
4640
- const tones = _chunkUADYAYATcjs.scaleTonesInDegreeOrder.call(void 0, key);
4641
- return tones.length > 0 ? _nullishCoalesce(tones[(degree - 1) % tones.length], () => ( key.rootPc % 12)) : key.rootPc % 12;
5155
+ const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, _chunk3R3QVKY5cjs.heptatonicFrameOf.call(void 0, key));
5156
+ return _nullishCoalesce(tones[degree - 1], () => ( key.rootPc % 12));
4642
5157
  }
4643
5158
  const offset = _nullishCoalesce(BORROWED_OFFSET[degree], () => ( 0));
4644
5159
  return ((key.rootPc % 12 + offset) % 12 + 12) % 12;
4645
5160
  }
4646
5161
  function autoQuality(degree, key, harmonicDominant) {
4647
5162
  if (degree >= 1 && degree <= 7) {
4648
- const isMinor = (key.modeMask12 >> 3 & 1) === 1 && (key.modeMask12 >> 4 & 1) === 0;
4649
- if (harmonicDominant && isMinor && degree === 5) {
5163
+ if (harmonicDominant && _chunkSGLEQVJHcjs.isMinorKey.call(void 0, key) && degree === 5) {
4650
5164
  return "maj";
4651
5165
  }
4652
- return _chunkUADYAYATcjs.diatonicTriad.call(void 0, degree, key).quality;
5166
+ return _chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, _chunk3R3QVKY5cjs.heptatonicFrameOf.call(void 0, key)).quality;
4653
5167
  }
4654
5168
  if (degree === 14) {
4655
5169
  return "dim";
@@ -4660,59 +5174,57 @@ function autoQuality(degree, key, harmonicDominant) {
4660
5174
  return "maj";
4661
5175
  }
4662
5176
  function resolveCycle(degrees, key, ext, harmonicDominant) {
4663
- const steps = [];
4664
- for (const degree of degrees) {
5177
+ const steps = degrees.map((degree) => {
4665
5178
  const step = {
4666
- source: degree,
4667
5179
  rootPc: degreeToRootPc(degree, key),
4668
5180
  quality: ext !== void 0 && ext !== "auto" ? ext : autoQuality(degree, key, harmonicDominant)
4669
5181
  };
4670
5182
  if (degree >= 1 && degree <= 7) {
4671
5183
  step.degree = degree;
4672
5184
  }
4673
- const previous = steps[steps.length - 1];
4674
- if (previous !== void 0 && previous.source !== step.source && sameStep(previous, step)) {
4675
- continue;
4676
- }
4677
- steps.push(step);
4678
- }
4679
- const first = steps[0];
4680
- const last = steps[steps.length - 1];
4681
- if (steps.length > 1 && first !== void 0 && last !== void 0 && first.source !== last.source && sameStep(first, last)) {
4682
- steps.pop();
4683
- }
4684
- return steps.length > 0 ? steps : [{ source: 0, degree: 0, rootPc: degreeToRootPc(0, key), quality: "maj" }];
5185
+ return step;
5186
+ });
5187
+ return steps.length > 0 ? steps : [{ rootPc: degreeToRootPc(1, key), quality: "maj" }];
4685
5188
  }
4686
- function sameStep(a, b) {
4687
- return a.rootPc === b.rootPc && a.quality === b.quality;
5189
+ function pickProgressionPreset(style, ctx = 0) {
5190
+ return presetFor(style, resolveContext(ctx));
4688
5191
  }
4689
- function pickProgressionPreset(style, seed = 0) {
5192
+ function presetFor(style, ctx) {
4690
5193
  const pool = PRESETS.filter((preset) => preset.styles.includes(style));
4691
5194
  if (pool.length === 0) {
4692
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`Unknown progression style: ${style}`);
5195
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Unknown progression style: ${style}`);
4693
5196
  }
4694
- const index = resolveContext(seed).part("progression").range(0, pool.length - 1, "preset");
5197
+ const index = ctx.part("progression").range(0, pool.length - 1, "preset");
4695
5198
  return _nullishCoalesce(pool[index], () => ( PRESETS[0]));
4696
5199
  }
5200
+ function tonicizableRootPcs(key) {
5201
+ return new Set(_chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key).map((degree) => degree.rootPc));
5202
+ }
5203
+ function canBeTonicized(span, tonicizable) {
5204
+ if (!tonicizable.has(span.rootPc)) {
5205
+ return false;
5206
+ }
5207
+ return _chunk3R3QVKY5cjs.fifthPcOf.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, span.rootPc, span.quality)) === (span.rootPc + 7) % 12;
5208
+ }
4697
5209
  function generateProgression(opts) {
4698
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, opts.bars, "progression bars");
4699
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, opts.bars, "progression chords");
4700
- const key = _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key);
5210
+ _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "progression options");
5211
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, opts.bars, "progression bars");
5212
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, opts.bars, "progression chords");
5213
+ const key = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, opts.key);
4701
5214
  const ctx = resolveContext(opts.ctx);
4702
- const seed = ctx.seed;
4703
5215
  let preset;
4704
5216
  if (opts.preset !== void 0) {
4705
5217
  if (opts.preset.degrees.length === 0) {
4706
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("progression preset must name at least one degree");
5218
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("progression preset must name at least one degree");
4707
5219
  }
4708
5220
  preset = {
4709
5221
  id: _nullishCoalesce(opts.preset.id, () => ( "custom")),
4710
5222
  name: _nullishCoalesce(opts.preset.name, () => ( "Custom")),
4711
5223
  degrees: opts.preset.degrees.map((degree, index) => {
4712
- _chunkQ2WGKJNNcjs.assertDegree.call(void 0, degree, `progression preset degrees[${index}]`);
5224
+ _chunkS5BKZZGMcjs.assertDegree.call(void 0, degree, `progression preset degrees[${index}]`);
4713
5225
  const supported = degree >= 1 && degree <= 7 || Object.values(BORROWED_DEGREES).includes(degree);
4714
5226
  if (!supported) {
4715
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
5227
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
4716
5228
  `progression preset degrees[${index}] is not a supported progression degree; received ${degree}`
4717
5229
  );
4718
5230
  }
@@ -4725,26 +5237,27 @@ function generateProgression(opts) {
4725
5237
  if (preset === void 0 && opts.presetId !== void 0) {
4726
5238
  preset = PRESETS.find((p) => p.id === opts.presetId);
4727
5239
  if (preset === void 0) {
4728
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`Unknown progression preset: ${opts.presetId}`);
5240
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Unknown progression preset: ${opts.presetId}`);
4729
5241
  }
4730
5242
  }
4731
- preset ??= pickProgressionPreset(opts.style, seed);
4732
- const ext = opts.ext === void 0 ? "auto" : _chunkQ2WGKJNNcjs.assertOneOf.call(void 0, opts.ext, ["auto", ..._chunkUADYAYATcjs.chordQualities.call(void 0, )], "progression extension");
5243
+ preset ??= presetFor(opts.style, ctx);
5244
+ const ext = opts.ext === void 0 ? "auto" : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, opts.ext, ["auto", ..._chunk3R3QVKY5cjs.chordQualities.call(void 0, )], "progression extension");
4733
5245
  const cycle = resolveCycle(
4734
- _nullishCoalesce(_optionalChain([preset, 'optionalAccess', _56 => _56.degrees]), () => ( [0])),
5246
+ _nullishCoalesce(_optionalChain([preset, 'optionalAccess', _59 => _59.degrees]), () => ( [0])),
4735
5247
  key,
4736
5248
  ext,
4737
- _optionalChain([preset, 'optionalAccess', _57 => _57.functional]) === "cadenceStrong"
5249
+ _optionalChain([preset, 'optionalAccess', _60 => _60.functional]) === "cadenceStrong"
4738
5250
  );
5251
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS6)));
4739
5252
  const chords = [];
4740
5253
  for (let bar2 = 0; bar2 < opts.bars; bar2 += 1) {
4741
5254
  const step = cycle[bar2 % cycle.length];
4742
5255
  const chord = {
4743
- rootPc: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _58 => _58.rootPc]), () => ( 0)),
4744
- quality: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _59 => _59.quality]), () => ( "maj")),
4745
- startBeat: bar2 * 4
5256
+ rootPc: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _61 => _61.rootPc]), () => ( 0)),
5257
+ quality: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _62 => _62.quality]), () => ( "maj")),
5258
+ startBeat: bar2 * barBeats
4746
5259
  };
4747
- if (_optionalChain([step, 'optionalAccess', _60 => _60.degree]) !== void 0) {
5260
+ if (_optionalChain([step, 'optionalAccess', _63 => _63.degree]) !== void 0) {
4748
5261
  chord.degree = step.degree;
4749
5262
  }
4750
5263
  chords.push(chord);
@@ -4752,6 +5265,7 @@ function generateProgression(opts) {
4752
5265
  const harmonic = _nullishCoalesce(ctx.harmonic, () => ( DEFAULT_HARMONIC));
4753
5266
  if (harmonic > 0) {
4754
5267
  const tonicPc = (key.rootPc % 12 + 12) % 12;
5268
+ const tonicizable = tonicizableRootPcs(key);
4755
5269
  const draw = ctx.part("progression");
4756
5270
  let tonicStatements = chords.filter((chord) => chord.rootPc === tonicPc).length;
4757
5271
  for (let i = 0; i < chords.length - 1; i += 1) {
@@ -4764,8 +5278,9 @@ function generateProgression(opts) {
4764
5278
  const targetsTonic = (next.rootPc - tonicPc + 12) % 12 === 0;
4765
5279
  const isLastTonicStatement = cur.rootPc === tonicPc && tonicStatements <= 1;
4766
5280
  const alreadySecondary = cur.rootPc === domRoot && cur.quality === "dom7";
4767
- const isResolutionTarget = _optionalChain([chords, 'access', _61 => _61[i - 1], 'optionalAccess', _62 => _62.secondaryDominant]) === true;
4768
- if (targetsTonic || isLastTonicStatement || alreadySecondary || isResolutionTarget) {
5281
+ const isResolutionTarget = _optionalChain([chords, 'access', _64 => _64[i - 1], 'optionalAccess', _65 => _65.secondaryDominant]) === true;
5282
+ const takesNoDominant = !canBeTonicized(next, tonicizable);
5283
+ if (targetsTonic || isLastTonicStatement || alreadySecondary || isResolutionTarget || takesNoDominant) {
4769
5284
  continue;
4770
5285
  }
4771
5286
  if (draw.prob(harmonic, "reharmonize", i)) {
@@ -4785,18 +5300,12 @@ function generateProgression(opts) {
4785
5300
  }
4786
5301
 
4787
5302
  // src/generate/reharmony/index.ts
4788
- function hasInterval(chord, semitone) {
4789
- return chord.intervals.some((iv) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, iv) === semitone);
4790
- }
4791
- function isDominantType(chord) {
4792
- return hasInterval(chord, 4) && hasInterval(chord, 10);
4793
- }
4794
5303
  function commonToneCount(a, b) {
4795
5304
  const set = new Set(b);
4796
5305
  return a.filter((pc) => set.has(pc)).length;
4797
5306
  }
4798
5307
  function triadQualityOf(chord) {
4799
- const semis = new Set(chord.intervals.map((interval) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, interval)));
5308
+ const semis = new Set(chord.intervals.map((interval) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval)));
4800
5309
  const hasThird = semis.has(4) ? "maj" : semis.has(3) ? "min" : void 0;
4801
5310
  if (hasThird === void 0) {
4802
5311
  return chord.quality;
@@ -4810,104 +5319,91 @@ function triadQualityOf(chord) {
4810
5319
  return hasThird;
4811
5320
  }
4812
5321
  function diatonicTriadsOf(key) {
4813
- const degrees = _chunkUADYAYATcjs.scaleTonesInDegreeOrder.call(void 0, key).length;
5322
+ const degrees = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key).length;
4814
5323
  const triads = [];
4815
5324
  for (let degree = 1; degree <= degrees; degree += 1) {
4816
- triads.push(_chunkUADYAYATcjs.diatonicTriad.call(void 0, degree, key));
5325
+ triads.push(_chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, key));
4817
5326
  }
4818
5327
  return triads;
4819
5328
  }
4820
5329
  function sameChord(a, b) {
4821
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, a.rootPc) === _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, b.rootPc) && a.quality === b.quality;
5330
+ return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, a.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, b.rootPc) && a.quality === b.quality;
4822
5331
  }
4823
5332
  var THIRD_OFFSETS = [3, 4, 8, 9];
4824
- var MIDDLE_C = 60;
4825
5333
  var MAX_HINT_ALTER = 2;
4826
5334
  function bareSpelling(note2) {
4827
5335
  return { letter: note2.letter, alter: note2.alter };
4828
5336
  }
4829
- function accidentalLoad(tonic, key) {
4830
- return _chunkUADYAYATcjs.spellScale.call(void 0, tonic, key).reduce((sum, note2) => sum + Math.abs(note2.alter), 0);
4831
- }
4832
- function keyTonicOf(key) {
4833
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc);
4834
- const sharp = bareSpelling(_chunkQ2WGKJNNcjs.midiToNote.call(void 0, MIDDLE_C + rootPc, "sharp"));
4835
- const flat = bareSpelling(_chunkQ2WGKJNNcjs.midiToNote.call(void 0, MIDDLE_C + rootPc, "flat"));
4836
- if (sharp.letter === flat.letter) {
4837
- return sharp;
4838
- }
4839
- return accidentalLoad(flat, key) <= accidentalLoad(sharp, key) ? flat : sharp;
4840
- }
4841
5337
  function spelledOnRoot(chord, root, tonic, key) {
4842
- const usable = _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, root) === _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc) && Math.abs(root.alter) <= MAX_HINT_ALTER;
5338
+ const usable = _chunkS5BKZZGMcjs.noteToPitchClass.call(void 0, root) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc) && Math.abs(root.alter) <= MAX_HINT_ALTER;
4843
5339
  const spelled = {
4844
5340
  ...chord,
4845
- rootSpelling: bareSpelling(usable ? root : _chunkUADYAYATcjs.spellPitchClass.call(void 0, chord.rootPc, tonic, key))
5341
+ rootSpelling: bareSpelling(usable ? root : _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, chord.rootPc, tonic, key))
4846
5342
  };
4847
5343
  if (chord.bassPc !== void 0) {
4848
- spelled.bassSpelling = bareSpelling(_chunkUADYAYATcjs.spellPitchClass.call(void 0, chord.bassPc, tonic, key));
5344
+ spelled.bassSpelling = bareSpelling(_chunk3R3QVKY5cjs.spellPitchClass.call(void 0, chord.bassPc, tonic, key));
4849
5345
  }
4850
5346
  return spelled;
4851
5347
  }
4852
5348
  function spelledInKey(chord, tonic, key) {
4853
- return spelledOnRoot(chord, _chunkUADYAYATcjs.spellPitchClass.call(void 0, chord.rootPc, tonic, key), tonic, key);
5349
+ return spelledOnRoot(chord, _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, chord.rootPc, tonic, key), tonic, key);
4854
5350
  }
4855
5351
  function tritoneSubstituteRoot(dominant, tonic, key) {
4856
- const substituteRootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, dominant.rootPc + 6);
4857
- if (_chunkUADYAYATcjs.isScaleTone.call(void 0, substituteRootPc, key)) {
4858
- return _chunkUADYAYATcjs.spellPitchClass.call(void 0, substituteRootPc, tonic, key);
5352
+ const substituteRootPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, dominant.rootPc + 6);
5353
+ if (_chunk3R3QVKY5cjs.isScaleTone.call(void 0, substituteRootPc, key)) {
5354
+ return _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, substituteRootPc, tonic, key);
4859
5355
  }
4860
- const resolution = _chunkUADYAYATcjs.spellPitchClass.call(void 0, _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, dominant.rootPc + 5), tonic, key);
4861
- return _chunkQ2WGKJNNcjs.transposeNote.call(void 0, resolution, 1);
5356
+ const resolution = _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, dominant.rootPc + 5), tonic, key);
5357
+ return _chunkS5BKZZGMcjs.transposeNote.call(void 0, resolution, 1);
4862
5358
  }
4863
5359
  function substituteChord(chord, key, opts) {
4864
- const source = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
4865
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
4866
- const originalTriad = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, _chunkUADYAYATcjs.makeChord.call(void 0, source.rootPc, triadQualityOf(source)));
5360
+ _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
5361
+ const source = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
5362
+ const { scale, tonic } = _chunk3R3QVKY5cjs.resolveKey.call(void 0, key);
5363
+ const originalTriad = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, source.rootPc, triadQualityOf(source)));
4867
5364
  const candidates = [];
4868
- const tonic = keyTonicOf(scale);
4869
- if (isDominantType(source)) {
4870
- const substitute = _chunkUADYAYATcjs.makeChord.call(void 0, _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, source.rootPc + 6), "dom7");
5365
+ if (_chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, source)) {
5366
+ const substitute = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, source.rootPc + 6), "dom7");
4871
5367
  candidates.push({
4872
5368
  chord: spelledOnRoot(substitute, tritoneSubstituteRoot(source, tonic, scale), tonic, scale),
4873
5369
  type: "tritone"
4874
5370
  });
4875
5371
  }
4876
5372
  for (const triad of diatonicTriadsOf(scale)) {
4877
- const offset = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, triad.rootPc - source.rootPc);
4878
- if (THIRD_OFFSETS.includes(offset) && commonToneCount(originalTriad, _chunkUADYAYATcjs.chordPitchClasses.call(void 0, triad)) === 2) {
5373
+ const offset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, triad.rootPc - source.rootPc);
5374
+ if (THIRD_OFFSETS.includes(offset) && commonToneCount(originalTriad, _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, triad)) === 2) {
4879
5375
  candidates.push({ chord: spelledInKey(triad, tonic, scale), type: "relative" });
4880
5376
  }
4881
5377
  }
4882
- const targetFunction = _chunkOE6WK2Y2cjs.functionOf.call(void 0, source, scale);
4883
- const parallel = _chunkOE6WK2Y2cjs.parallelKey.call(void 0, scale);
5378
+ const targetFunction = _chunkSGLEQVJHcjs.functionOf.call(void 0, source, scale);
5379
+ const parallel = _chunkSGLEQVJHcjs.parallelKey.call(void 0, scale);
4884
5380
  for (const triad of diatonicTriadsOf(parallel)) {
4885
- if (_chunkOE6WK2Y2cjs.functionOf.call(void 0, triad, scale) === targetFunction && !_chunkOE6WK2Y2cjs.isDiatonic.call(void 0, triad, scale)) {
5381
+ if (_chunkSGLEQVJHcjs.functionOf.call(void 0, triad, scale) === targetFunction && !_chunkSGLEQVJHcjs.isDiatonic.call(void 0, triad, scale)) {
4886
5382
  candidates.push({ chord: spelledInKey(triad, tonic, parallel), type: "borrowed" });
4887
5383
  }
4888
5384
  }
4889
5385
  for (const offset of THIRD_OFFSETS) {
4890
5386
  for (const quality of ["maj", "min"]) {
4891
- const mediant = _chunkUADYAYATcjs.makeChord.call(void 0, _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, source.rootPc + offset), quality);
4892
- if (commonToneCount(originalTriad, _chunkUADYAYATcjs.chordPitchClasses.call(void 0, mediant)) === 1 && !_chunkOE6WK2Y2cjs.isDiatonic.call(void 0, mediant, scale)) {
5387
+ const mediant = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, source.rootPc + offset), quality);
5388
+ if (commonToneCount(originalTriad, _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, mediant)) === 1 && !_chunkSGLEQVJHcjs.isDiatonic.call(void 0, mediant, scale)) {
4893
5389
  candidates.push({ chord: spelledInKey(mediant, tonic, scale), type: "chromaticMediant" });
4894
5390
  }
4895
5391
  }
4896
5392
  }
4897
- const melodyPcs = _optionalChain([opts, 'optionalAccess', _63 => _63.melodyPcs, 'optionalAccess', _64 => _64.map, 'call', _65 => _65(_chunkQ2WGKJNNcjs.pitchClassOf)]);
5393
+ const melodyPcs = _optionalChain([opts, 'optionalAccess', _66 => _66.melodyPcs, 'optionalAccess', _67 => _67.map, 'call', _68 => _68(_chunkS5BKZZGMcjs.pitchClassOf)]);
4898
5394
  const seen = /* @__PURE__ */ new Set();
4899
5395
  const results = [];
4900
5396
  for (const candidate of candidates) {
4901
5397
  if (sameChord(candidate.chord, source)) {
4902
5398
  continue;
4903
5399
  }
4904
- const dedupeKey = `${_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, candidate.chord.rootPc)}:${candidate.chord.quality}`;
5400
+ const dedupeKey = `${_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, candidate.chord.rootPc)}:${candidate.chord.quality}`;
4905
5401
  if (seen.has(dedupeKey)) {
4906
5402
  continue;
4907
5403
  }
4908
5404
  seen.add(dedupeKey);
4909
5405
  if (melodyPcs !== void 0) {
4910
- const pcs = new Set(_chunkUADYAYATcjs.chordPitchClasses.call(void 0, candidate.chord));
5406
+ const pcs = new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, candidate.chord));
4911
5407
  if (!melodyPcs.every((pc) => pcs.has(pc))) {
4912
5408
  continue;
4913
5409
  }
@@ -4915,67 +5411,65 @@ function substituteChord(chord, key, opts) {
4915
5411
  results.push({
4916
5412
  chord: candidate.chord,
4917
5413
  type: candidate.type,
4918
- roman: _chunkOE6WK2Y2cjs.chordToRoman.call(void 0, candidate.chord, scale),
4919
- function: _chunkOE6WK2Y2cjs.functionOf.call(void 0, candidate.chord, scale)
5414
+ roman: _chunkSGLEQVJHcjs.chordToRoman.call(void 0, candidate.chord, scale),
5415
+ function: _chunkSGLEQVJHcjs.functionOf.call(void 0, candidate.chord, scale)
4920
5416
  });
4921
5417
  }
4922
5418
  return results;
4923
5419
  }
4924
5420
  function modalInterchangePalette(key) {
4925
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
5421
+ const { scale, tonic } = _chunk3R3QVKY5cjs.resolveKey.call(void 0, key);
4926
5422
  const palette = [];
4927
5423
  const seen = /* @__PURE__ */ new Set();
4928
5424
  const add = (chord) => {
4929
- const dedupeKey = `${_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc)}:${chord.quality}`;
5425
+ const dedupeKey = `${_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc)}:${chord.quality}`;
4930
5426
  if (seen.has(dedupeKey)) {
4931
5427
  return;
4932
5428
  }
4933
5429
  seen.add(dedupeKey);
4934
5430
  palette.push({
4935
5431
  chord,
4936
- roman: _chunkOE6WK2Y2cjs.chordToRoman.call(void 0, chord, scale),
4937
- source: _chunkOE6WK2Y2cjs.borrowedSource.call(void 0, chord, scale)
5432
+ roman: _chunkSGLEQVJHcjs.chordToRoman.call(void 0, chord, scale),
5433
+ source: _chunkSGLEQVJHcjs.borrowedSource.call(void 0, chord, scale)
4938
5434
  });
4939
5435
  };
4940
- const tonic = keyTonicOf(scale);
4941
- const parallel = _chunkOE6WK2Y2cjs.parallelKey.call(void 0, scale);
5436
+ const parallel = _chunkSGLEQVJHcjs.parallelKey.call(void 0, scale);
4942
5437
  for (const triad of diatonicTriadsOf(parallel)) {
4943
- if (!_chunkOE6WK2Y2cjs.isDiatonic.call(void 0, triad, scale) && _chunkOE6WK2Y2cjs.borrowedSource.call(void 0, triad, scale) !== null) {
5438
+ if (!_chunkSGLEQVJHcjs.isDiatonic.call(void 0, triad, scale) && _chunkSGLEQVJHcjs.borrowedSource.call(void 0, triad, scale) !== null) {
4944
5439
  add(spelledInKey(triad, tonic, parallel));
4945
5440
  }
4946
5441
  }
4947
- const neapolitan = _chunkUADYAYATcjs.makeChord.call(void 0, _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, scale.rootPc + 1), "maj");
4948
- if (!_chunkOE6WK2Y2cjs.isDiatonic.call(void 0, neapolitan, scale)) {
4949
- add(spelledOnRoot(neapolitan, _chunkQ2WGKJNNcjs.transposeNote.call(void 0, tonic, 1), tonic, scale));
5442
+ const neapolitan = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, scale.rootPc + 1), "maj");
5443
+ if (!_chunkSGLEQVJHcjs.isDiatonic.call(void 0, neapolitan, scale)) {
5444
+ add(spelledOnRoot(neapolitan, _chunkS5BKZZGMcjs.transposeNote.call(void 0, tonic, 1), tonic, scale));
4950
5445
  }
4951
5446
  return palette;
4952
5447
  }
4953
5448
  function negativeHarmonyMirror(chord, key) {
4954
- const source = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
4955
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
4956
- const tonicPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, scale.rootPc);
4957
- const tonic = keyTonicOf(scale);
4958
- const mirror = (p) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, 2 * tonicPc + 7 - p);
4959
- const pcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, source).map(mirror);
5449
+ const source = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
5450
+ const { scale, tonic } = _chunk3R3QVKY5cjs.resolveKey.call(void 0, key);
5451
+ const tonicPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, scale.rootPc);
5452
+ const mirror = (p) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, 2 * tonicPc + 7 - p);
5453
+ const pcs = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, source).map(mirror);
4960
5454
  const bassPc = source.bassPc !== void 0 ? mirror(source.bassPc) : void 0;
4961
- const pitches = bassPc !== void 0 ? [bassPc - 12, ...pcs] : pcs;
4962
- const detected = _chunkOE6WK2Y2cjs.detectChordBest.call(void 0, pitches);
5455
+ const pitches = bassPc !== void 0 ? [bassPc, ...pcs.map((pc) => pc + 12)] : pcs;
5456
+ const detected = _chunkSGLEQVJHcjs.detectChordBest.call(void 0, pitches);
4963
5457
  if (detected) {
4964
5458
  if (bassPc === void 0 && detected.bassPc !== void 0) {
4965
- return spelledInKey(_chunkUADYAYATcjs.makeChord.call(void 0, detected.rootPc, detected.quality), tonic, scale);
5459
+ return spelledInKey(_chunk3R3QVKY5cjs.makeChord.call(void 0, detected.rootPc, detected.quality), tonic, scale);
4966
5460
  }
4967
5461
  return spelledInKey(detected, tonic, scale);
4968
5462
  }
4969
5463
  const quality = source.quality;
4970
- return spelledInKey(_chunkUADYAYATcjs.makeChord.call(void 0, mirror(source.rootPc), quality, bassPc), tonic, scale);
5464
+ return spelledInKey(_chunk3R3QVKY5cjs.makeChord.call(void 0, mirror(source.rootPc), quality, bassPc), tonic, scale);
4971
5465
  }
4972
5466
 
4973
5467
  // src/generate/rhythm/index.ts
4974
5468
  var DEFAULT_RHYTHM_VELOCITY = 96;
4975
5469
  function rhythmToNoteEvents(events, pitch, velocity = DEFAULT_RHYTHM_VELOCITY) {
4976
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, pitch, "pitch");
4977
- _chunkQ2WGKJNNcjs.assertRange.call(void 0, velocity, 0, 127, "velocity");
4978
- return events.map((event) => ({
5470
+ _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch, "pitch");
5471
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, velocity, "velocity", 0, 127);
5472
+ return _chunkS5BKZZGMcjs.assertArray.call(void 0, events, "events").map((event) => ({
4979
5473
  pitch,
4980
5474
  startBeat: event.position,
4981
5475
  durationBeat: event.duration,
@@ -4986,6 +5480,7 @@ var DEFAULT_SUBDIVISION = 2;
4986
5480
  var DEFAULT_DENSITY = 0.5;
4987
5481
  var DEFAULT_BARS = 1;
4988
5482
  function onsetWeightCurve(weight) {
5483
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0, weight, "weight", 0, 3);
4989
5484
  switch (weight) {
4990
5485
  case 3:
4991
5486
  return 1;
@@ -4998,18 +5493,20 @@ function onsetWeightCurve(weight) {
4998
5493
  }
4999
5494
  }
5000
5495
  function generateRhythm(ts, opts = {}) {
5001
- const bars = _nullishCoalesce(opts.bars, () => ( DEFAULT_BARS));
5002
- const subdivision = _nullishCoalesce(opts.subdivision, () => ( DEFAULT_SUBDIVISION));
5003
- const ctx = resolveContext(opts.ctx);
5496
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
5497
+ const meter = _chunkS5BKZZGMcjs.meterAt.call(void 0, 0, _chunkS5BKZZGMcjs.toMeterData.call(void 0, ts, "ts"));
5498
+ const bars = _nullishCoalesce(asked.bars, () => ( DEFAULT_BARS));
5499
+ const subdivision = _nullishCoalesce(asked.subdivision, () => ( DEFAULT_SUBDIVISION));
5500
+ const ctx = resolveContext(asked.ctx);
5004
5501
  const density = _nullishCoalesce(ctx.rhythmic, () => ( DEFAULT_DENSITY));
5005
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, bars, "rhythm bars");
5006
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, subdivision, "rhythm subdivision");
5007
- const barBeats = _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
5502
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, bars, "rhythm bars");
5503
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, subdivision, "rhythm subdivision");
5504
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, meter);
5008
5505
  const spanBeats = barBeats * bars;
5009
5506
  const step = 1 / subdivision;
5010
5507
  const slotsPerBar = Math.ceil(barBeats * subdivision);
5011
5508
  const slotCount = slotsPerBar * bars;
5012
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, slotCount, "rhythm grid slots");
5509
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slotCount, "rhythm grid slots");
5013
5510
  const draw = ctx.part("rhythm");
5014
5511
  const positions = [];
5015
5512
  for (let bar2 = 0; bar2 < bars; bar2 += 1) {
@@ -5024,7 +5521,7 @@ function generateRhythm(ts, opts = {}) {
5024
5521
  positions.push(position);
5025
5522
  continue;
5026
5523
  }
5027
- const weight = _chunkK4ZJUGF4cjs.metricWeight.call(void 0, position, ts);
5524
+ const weight = _chunkS5BKZZGMcjs.metricWeight.call(void 0, position, meter);
5028
5525
  const probability = onsetWeightCurve(weight) * density;
5029
5526
  if (draw.prob(probability, "onset", bar2, slot)) {
5030
5527
  positions.push(position);
@@ -5040,12 +5537,14 @@ function generateRhythm(ts, opts = {}) {
5040
5537
  return events;
5041
5538
  }
5042
5539
  function rhythmDensity(events, ts) {
5540
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, events, "events");
5541
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBar.call(void 0, ts);
5043
5542
  if (events.length === 0) {
5044
5543
  return 0;
5045
5544
  }
5046
5545
  const last = events[events.length - 1];
5047
- const spanBeats = (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _66 => _66.position]), () => ( 0))) + (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _67 => _67.duration]), () => ( 0)));
5048
- const bars = spanBeats / _chunkK4ZJUGF4cjs.beatsPerBar.call(void 0, ts);
5546
+ const spanBeats = (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _69 => _69.position]), () => ( 0))) + (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _70 => _70.duration]), () => ( 0)));
5547
+ const bars = spanBeats / barBeats;
5049
5548
  return bars > 0 ? events.length / bars : 0;
5050
5549
  }
5051
5550
 
@@ -5117,5 +5616,6 @@ function rhythmDensity(events, ts) {
5117
5616
 
5118
5617
 
5119
5618
 
5120
- exports.GENRES = GENRES; exports.PROVENANCE_BASES = PROVENANCE_BASES; exports.fitsQuery = fitsQuery; exports.selectVocabulary = selectVocabulary; exports.pickVocabulary = pickVocabulary; exports.mergeVocabulary = mergeVocabulary; exports.vocabularyOfKind = vocabularyOfKind; exports.assertVocabulary = assertVocabulary; exports.MIN_DIFFICULTY = MIN_DIFFICULTY; exports.MAX_DIFFICULTY = MAX_DIFFICULTY; exports.sustainsStrokes = sustainsStrokes; exports.sustainsShift = sustainsShift; exports.resolveContext = resolveContext; exports.BAR_STEPS = BAR_STEPS; exports.BEAT_STEPS = BEAT_STEPS; exports.STEP_BEATS = STEP_BEATS; exports.gridMetricWeight = gridMetricWeight; exports.thin = thin; exports.double = double; exports.halfTime = halfTime; exports.doubleTime = doubleTime; exports.syncopate = syncopate; exports.ornamentBy = ornamentBy; exports.deform = deform; exports.withinCeiling = withinCeiling; exports.isLickMaterial = isLickMaterial; exports.BASS_LICKS = BASS_LICKS; exports.placeLicks = placeLicks; exports.BASS_STYLES = BASS_STYLES; exports.generateBassLine = generateBassLine; exports.imitate = imitate; exports.generateCounterMelody = generateCounterMelody; exports.FILL_TYPES = FILL_TYPES; exports.FILL_ARCHETYPES = FILL_ARCHETYPES; exports.isFillArchetype = isFillArchetype; exports.DRUM_NOTES = DRUM_NOTES; exports.drumVoiceOf = drumVoiceOf; exports.KICK_STEPS = KICK_STEPS; exports.KICK_FIGURES = KICK_FIGURES; exports.DRUM_KIT = DRUM_KIT; exports.isDrumPattern = isDrumPattern; exports.DRUM_PATTERNS = DRUM_PATTERNS; exports.placeDrumPattern = placeDrumPattern; exports.generateDrums = generateDrums; exports.humanize = humanize; exports.extractGrooveTemplate = extractGrooveTemplate; exports.applyGrooveTemplate = applyGrooveTemplate; exports.classifyMelodyTones = classifyMelodyTones; exports.harmonizeMelody = harmonizeMelody; exports.generateMotif = generateMotif; exports.motifToNoteEvents = motifToNoteEvents; exports.transformMotif = transformMotif; exports.developMotif = developMotif; exports.ORNAMENT_STYLES = ORNAMENT_STYLES; exports.ornament = ornament; exports.BORROWED_DEGREES = BORROWED_DEGREES; exports.progressions = progressions; exports.progressionsByStyle = progressionsByStyle; exports.pickProgressionPreset = pickProgressionPreset; exports.generateProgression = generateProgression; exports.substituteChord = substituteChord; exports.modalInterchangePalette = modalInterchangePalette; exports.negativeHarmonyMirror = negativeHarmonyMirror; exports.rhythmToNoteEvents = rhythmToNoteEvents; exports.onsetWeightCurve = onsetWeightCurve; exports.generateRhythm = generateRhythm; exports.rhythmDensity = rhythmDensity;
5121
- //# sourceMappingURL=chunk-AFM2ULJH.cjs.map
5619
+
5620
+ exports.GENRES = GENRES; exports.PROVENANCE_BASES = PROVENANCE_BASES; exports.fitsQuery = fitsQuery; exports.selectVocabulary = selectVocabulary; exports.pickVocabulary = pickVocabulary; exports.mergeVocabulary = mergeVocabulary; exports.vocabularyOfKind = vocabularyOfKind; exports.assertVocabulary = assertVocabulary; exports.MIN_DIFFICULTY = MIN_DIFFICULTY; exports.MAX_DIFFICULTY = MAX_DIFFICULTY; exports.sustainsStrokes = sustainsStrokes; exports.sustainsShift = sustainsShift; exports.resolveContext = resolveContext; exports.BAR_STEPS = BAR_STEPS; exports.BEAT_STEPS = BEAT_STEPS; exports.STEP_BEATS = STEP_BEATS; exports.gridMetricWeight = gridMetricWeight; exports.thin = thin; exports.double = double; exports.halfTime = halfTime; exports.doubleTime = doubleTime; exports.syncopate = syncopate; exports.ornamentBy = ornamentBy; exports.deform = deform; exports.withinCeiling = withinCeiling; exports.isLickMaterial = isLickMaterial; exports.BASS_LICKS = BASS_LICKS; exports.placeLicks = placeLicks; exports.BASS_STYLES = BASS_STYLES; exports.generateBassLine = generateBassLine; exports.imitate = imitate; exports.generateCounterMelody = generateCounterMelody; exports.FILL_TYPES = FILL_TYPES; exports.FILL_ARCHETYPES = FILL_ARCHETYPES; exports.isFillArchetype = isFillArchetype; exports.DRUM_NOTES = DRUM_NOTES; exports.drumVoiceOf = drumVoiceOf; exports.KICK_STEPS = KICK_STEPS; exports.KICK_FIGURES = KICK_FIGURES; exports.DRUM_KIT = DRUM_KIT; exports.isDrumPattern = isDrumPattern; exports.DRUM_PATTERNS = DRUM_PATTERNS; exports.placeDrumPattern = placeDrumPattern; exports.generateDrums = generateDrums; exports.humanize = humanize; exports.extractGrooveTemplate = extractGrooveTemplate; exports.applyGrooveTemplate = applyGrooveTemplate; exports.classifyMelodyTones = classifyMelodyTones; exports.harmonizeMelody = harmonizeMelody; exports.cellSpan = cellSpan; exports.generateMotif = generateMotif; exports.motifToNoteEvents = motifToNoteEvents; exports.transformMotif = transformMotif; exports.developMotif = developMotif; exports.ORNAMENT_STYLES = ORNAMENT_STYLES; exports.ornament = ornament; exports.BORROWED_DEGREES = BORROWED_DEGREES; exports.progressions = progressions; exports.progressionsByStyle = progressionsByStyle; exports.pickProgressionPreset = pickProgressionPreset; exports.generateProgression = generateProgression; exports.substituteChord = substituteChord; exports.modalInterchangePalette = modalInterchangePalette; exports.negativeHarmonyMirror = negativeHarmonyMirror; exports.rhythmToNoteEvents = rhythmToNoteEvents; exports.onsetWeightCurve = onsetWeightCurve; exports.generateRhythm = generateRhythm; exports.rhythmDensity = rhythmDensity;
5621
+ //# sourceMappingURL=chunk-J7FHVZII.cjs.map