@libraz/libcantus 0.9.4 → 0.9.5

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 (120) hide show
  1. package/README.md +27 -18
  2. package/README_ja.md +268 -0
  3. package/dist/analyze/index.cjs +7 -7
  4. package/dist/analyze/index.d.cts +9 -99
  5. package/dist/analyze/index.d.ts +9 -99
  6. package/dist/analyze/index.js +6 -6
  7. package/dist/{chunk-36N73KZ3.cjs → chunk-2A2QMA74.cjs} +80 -65
  8. package/dist/chunk-2A2QMA74.cjs.map +1 -0
  9. package/dist/{chunk-YLEMGEFH.js → chunk-2PTFGVD7.js} +50 -35
  10. package/dist/chunk-2PTFGVD7.js.map +1 -0
  11. package/dist/{chunk-ZEJR4TKL.cjs → chunk-357ZDPVU.cjs} +36 -47
  12. package/dist/chunk-357ZDPVU.cjs.map +1 -0
  13. package/dist/{chunk-EHVEBWM5.js → chunk-3KX76RLG.js} +136 -37
  14. package/dist/chunk-3KX76RLG.js.map +1 -0
  15. package/dist/{chunk-RC6GBMCD.cjs → chunk-4VZ5MZAM.cjs} +155 -111
  16. package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
  17. package/dist/{chunk-RK35RIN7.js → chunk-7EONYVI5.js} +115 -28
  18. package/dist/chunk-7EONYVI5.js.map +1 -0
  19. package/dist/{chunk-GQSGS2N4.js → chunk-7JEP3MFY.js} +5 -16
  20. package/dist/chunk-7JEP3MFY.js.map +1 -0
  21. package/dist/{chunk-7NKDPF74.js → chunk-AID75RN3.js} +203 -104
  22. package/dist/chunk-AID75RN3.js.map +1 -0
  23. package/dist/{chunk-FNDWXRRI.cjs → chunk-BSLTLGM3.cjs} +55 -37
  24. package/dist/chunk-BSLTLGM3.cjs.map +1 -0
  25. package/dist/{chunk-NP4NA2FB.cjs → chunk-CU6VG2YM.cjs} +39 -36
  26. package/dist/chunk-CU6VG2YM.cjs.map +1 -0
  27. package/dist/{chunk-NYYWMV2C.js → chunk-DZMHIGI4.js} +27 -17
  28. package/dist/chunk-DZMHIGI4.js.map +1 -0
  29. package/dist/{chunk-I24MV65U.js → chunk-EDOTEABK.js} +18 -15
  30. package/dist/{chunk-I24MV65U.js.map → chunk-EDOTEABK.js.map} +1 -1
  31. package/dist/{chunk-NKIBQ6PT.js → chunk-HN2OEUN2.js} +7 -4
  32. package/dist/chunk-HN2OEUN2.js.map +1 -0
  33. package/dist/{chunk-D2FAJBYL.js → chunk-KBTTEX7Q.js} +30 -12
  34. package/dist/chunk-KBTTEX7Q.js.map +1 -0
  35. package/dist/{chunk-CAN76NCU.cjs → chunk-KQN2ZYND.cjs} +147 -102
  36. package/dist/chunk-KQN2ZYND.cjs.map +1 -0
  37. package/dist/{chunk-4HLLZPXT.cjs → chunk-MPTVJIVG.cjs} +350 -251
  38. package/dist/chunk-MPTVJIVG.cjs.map +1 -0
  39. package/dist/{chunk-RUMU3UA6.cjs → chunk-MTMZTMWM.cjs} +12 -12
  40. package/dist/{chunk-RUMU3UA6.cjs.map → chunk-MTMZTMWM.cjs.map} +1 -1
  41. package/dist/{chunk-KMHOICIY.js → chunk-NNB67XA2.js} +90 -45
  42. package/dist/chunk-NNB67XA2.js.map +1 -0
  43. package/dist/{chunk-LW53AHQU.cjs → chunk-OL3IHHSJ.cjs} +228 -141
  44. package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
  45. package/dist/{chunk-7MGZ7D62.cjs → chunk-PI3UXRWK.cjs} +43 -13
  46. package/dist/chunk-PI3UXRWK.cjs.map +1 -0
  47. package/dist/{chunk-XEBG3G53.js → chunk-R2VVFAIN.js} +42 -12
  48. package/dist/chunk-R2VVFAIN.js.map +1 -0
  49. package/dist/{chunk-Q6OP4U43.cjs → chunk-WFDXOLUB.cjs} +50 -40
  50. package/dist/chunk-WFDXOLUB.cjs.map +1 -0
  51. package/dist/{chunk-NVE34YGZ.cjs → chunk-WTAN4ACG.cjs} +212 -113
  52. package/dist/chunk-WTAN4ACG.cjs.map +1 -0
  53. package/dist/{chunk-FCANXBAT.cjs → chunk-YFJXS3AU.cjs} +8 -5
  54. package/dist/chunk-YFJXS3AU.cjs.map +1 -0
  55. package/dist/{chunk-Q2XUP227.js → chunk-YUE6TJ4M.js} +89 -45
  56. package/dist/chunk-YUE6TJ4M.js.map +1 -0
  57. package/dist/{chunk-HKGYCQ34.js → chunk-ZGLJM4A4.js} +2 -2
  58. package/dist/core/index.cjs +15 -5
  59. package/dist/core/index.cjs.map +1 -1
  60. package/dist/core/index.d.cts +14 -4
  61. package/dist/core/index.d.ts +14 -4
  62. package/dist/core/index.js +14 -4
  63. package/dist/{function-DNugAOrt.d.cts → function-CxixTB02.d.cts} +98 -7
  64. package/dist/{function-BtiZb9i9.d.ts → function-DTCt675z.d.ts} +98 -7
  65. package/dist/generate/index.cjs +10 -10
  66. package/dist/generate/index.d.cts +21 -21
  67. package/dist/generate/index.d.ts +21 -21
  68. package/dist/generate/index.js +9 -9
  69. package/dist/{index-tJhkmQgE.d.cts → index-B2Ulxxpo.d.cts} +3 -2
  70. package/dist/{index-Be74mcon.d.cts → index-BkYJ0xMq.d.cts} +2 -2
  71. package/dist/{index-6VR8-kit.d.ts → index-BuaB-ypt.d.ts} +3 -2
  72. package/dist/{index-70p168VM.d.ts → index-CAN7v-kw.d.ts} +9 -2
  73. package/dist/{index-C83mZI1P.d.ts → index-COw1Ky9g.d.ts} +1 -1
  74. package/dist/{index-tSQh-xwY.d.ts → index-Cfx8xgH0.d.ts} +1 -1
  75. package/dist/{index-C5UBbQ4G.d.ts → index-DRtIhlJj.d.ts} +2 -2
  76. package/dist/{index-BtXT6UQ0.d.cts → index-alrRninU.d.cts} +9 -2
  77. package/dist/{index-y_ak9_Hi.d.cts → index-oTQbYYIs.d.cts} +1 -1
  78. package/dist/{index-OG65ocOr.d.ts → index-tR9jTn26.d.cts} +4 -1
  79. package/dist/{index-OG65ocOr.d.cts → index-tR9jTn26.d.ts} +4 -1
  80. package/dist/{index-D6b6na69.d.cts → index-x2vcsduE.d.cts} +1 -1
  81. package/dist/index.cjs +24 -14
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.d.cts +11 -11
  84. package/dist/index.d.ts +11 -11
  85. package/dist/index.js +23 -13
  86. package/dist/model/index.cjs +7 -7
  87. package/dist/model/index.d.cts +48 -16
  88. package/dist/model/index.d.ts +48 -16
  89. package/dist/model/index.js +6 -6
  90. package/dist/{styled-DIRkk9Uz.d.ts → styled-Bl0ZK28h.d.ts} +40 -3
  91. package/dist/{styled-DJbkJQfj.d.cts → styled-Crv6SA4S.d.cts} +40 -3
  92. package/dist/theory/index.cjs +6 -6
  93. package/dist/theory/index.d.cts +9 -9
  94. package/dist/theory/index.d.ts +9 -9
  95. package/dist/theory/index.js +5 -5
  96. package/package.json +12 -9
  97. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  98. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  99. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  100. package/dist/chunk-7NKDPF74.js.map +0 -1
  101. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  102. package/dist/chunk-D2FAJBYL.js.map +0 -1
  103. package/dist/chunk-EHVEBWM5.js.map +0 -1
  104. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  105. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  106. package/dist/chunk-GQSGS2N4.js.map +0 -1
  107. package/dist/chunk-KMHOICIY.js.map +0 -1
  108. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  109. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  110. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  111. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  112. package/dist/chunk-NYYWMV2C.js.map +0 -1
  113. package/dist/chunk-Q2XUP227.js.map +0 -1
  114. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  115. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  116. package/dist/chunk-RK35RIN7.js.map +0 -1
  117. package/dist/chunk-XEBG3G53.js.map +0 -1
  118. package/dist/chunk-YLEMGEFH.js.map +0 -1
  119. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  120. /package/dist/{chunk-HKGYCQ34.js.map → chunk-ZGLJM4A4.js.map} +0 -0
@@ -1,25 +1,25 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
2
 
3
- var _chunkRUMU3UA6cjs = require('./chunk-RUMU3UA6.cjs');
3
+ var _chunkMTMZTMWMcjs = require('./chunk-MTMZTMWM.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
8
 
9
9
 
10
- var _chunk36N73KZ3cjs = require('./chunk-36N73KZ3.cjs');
11
10
 
11
+ var _chunk2A2QMA74cjs = require('./chunk-2A2QMA74.cjs');
12
12
 
13
13
 
14
- var _chunkCAN76NCUcjs = require('./chunk-CAN76NCU.cjs');
15
14
 
15
+ var _chunkKQN2ZYNDcjs = require('./chunk-KQN2ZYND.cjs');
16
16
 
17
- var _chunkFCANXBATcjs = require('./chunk-FCANXBAT.cjs');
18
17
 
18
+ var _chunkYFJXS3AUcjs = require('./chunk-YFJXS3AU.cjs');
19
19
 
20
20
 
21
- var _chunkQ6OP4U43cjs = require('./chunk-Q6OP4U43.cjs');
22
21
 
22
+ var _chunkWFDXOLUBcjs = require('./chunk-WFDXOLUB.cjs');
23
23
 
24
24
 
25
25
 
@@ -32,9 +32,9 @@ var _chunkQ6OP4U43cjs = require('./chunk-Q6OP4U43.cjs');
32
32
 
33
33
 
34
34
 
35
- var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
36
35
 
37
36
 
37
+ var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
38
38
 
39
39
 
40
40
 
@@ -45,10 +45,21 @@ var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
45
45
 
46
46
 
47
47
 
48
- var _chunk7MGZ7D62cjs = require('./chunk-7MGZ7D62.cjs');
48
+
49
+
50
+
51
+
52
+
53
+ var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
49
54
 
50
55
  // src/generate/bass/index.ts
51
- var BASS_STYLES = ["root", "rootFifth", "pop", "walking", "arpeggio"];
56
+ var BASS_STYLES = Object.freeze([
57
+ "root",
58
+ "rootFifth",
59
+ "pop",
60
+ "walking",
61
+ "arpeggio"
62
+ ]);
52
63
  var DEFAULT_TS = { numerator: 4, denominator: 4 };
53
64
  var DEFAULT_STYLE = "root";
54
65
  var DEFAULT_OCTAVE = 2;
@@ -71,29 +82,43 @@ function chordTonePcs(chord) {
71
82
  const seen = /* @__PURE__ */ new Set();
72
83
  const pcs = [];
73
84
  for (const interval of chord.intervals) {
74
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + interval);
85
+ const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
75
86
  if (!seen.has(pc)) {
76
87
  seen.add(pc);
77
88
  pcs.push(pc);
78
89
  }
79
90
  }
80
91
  if (pcs.length === 0) {
81
- pcs.push(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc));
92
+ pcs.push(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc));
82
93
  }
83
94
  return pcs;
84
95
  }
85
96
  function fifthPcOf(chord) {
86
- const fifths = new Set(chord.intervals.map((i) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, i)));
97
+ const fifths = new Set(chord.intervals.map((i) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, i)));
87
98
  const chosen = fifths.has(7) ? 7 : fifths.has(6) ? 6 : fifths.has(8) ? 8 : void 0;
88
- return chosen === void 0 ? _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc) : _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + chosen);
99
+ return chosen === void 0 ? _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc) : _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + chosen);
89
100
  }
90
101
  function bassPcOf(chord) {
91
- return _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
102
+ return _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
92
103
  }
93
- function beatPositions(start, end) {
104
+ function beatPositions(start, end, ts) {
94
105
  const positions = [];
95
- for (let p = start; p < end - EPS; p += 1) {
96
- positions.push(p);
106
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
107
+ const pulse = _chunk2A2QMA74cjs.pulseBeats.call(void 0, ts);
108
+ const firstBar = Math.floor(start / barBeats);
109
+ const lastBar = Math.floor((end - EPS) / barBeats);
110
+ for (let bar = firstBar; bar <= lastBar; bar += 1) {
111
+ const barStart = bar * barBeats;
112
+ const firstPulse = Math.max(0, Math.ceil((start - barStart - EPS) / pulse));
113
+ for (let index = firstPulse; ; index += 1) {
114
+ const position = barStart + index * pulse;
115
+ if (position >= barStart + barBeats - EPS || position >= end - EPS) {
116
+ break;
117
+ }
118
+ if (position >= start - EPS) {
119
+ positions.push(position);
120
+ }
121
+ }
97
122
  }
98
123
  if (positions.length === 0) {
99
124
  positions.push(start);
@@ -101,8 +126,11 @@ function beatPositions(start, end) {
101
126
  return positions;
102
127
  }
103
128
  function emit(ctx, pos, pc, midiOverride) {
104
- const midi = _nullishCoalesce(midiOverride, () => ( placePc(pc, ctx.prevMidi, ctx.low)));
105
- const velocity = _chunk36N73KZ3cjs.isStrongBeat.call(void 0, pos, ctx.ts) ? STRONG_VELOCITY : WEAK_VELOCITY;
129
+ const midi = _chunkPI3UXRWKcjs.clampToMidi.call(void 0,
130
+ _nullishCoalesce(midiOverride, () => ( placePc(pc, ctx.prevMidi, ctx.low))),
131
+ "generated bass pitch"
132
+ );
133
+ const velocity = _chunk2A2QMA74cjs.isStrongBeat.call(void 0, pos, ctx.ts) ? STRONG_VELOCITY : WEAK_VELOCITY;
106
134
  ctx.notes.push({ startBeat: pos, pitch: midi, velocity });
107
135
  return midi;
108
136
  }
@@ -117,11 +145,11 @@ function buildRootFifth(ctx, seg) {
117
145
  }
118
146
  }
119
147
  function buildPop(ctx, seg) {
120
- const positions = beatPositions(seg.startBeat, seg.endBeat);
148
+ const positions = beatPositions(seg.startBeat, seg.endBeat, ctx.ts);
121
149
  const rootPc = bassPcOf(seg.chord);
122
150
  let emitted = false;
123
151
  for (const pos of positions) {
124
- if (_chunk36N73KZ3cjs.isStrongBeat.call(void 0, pos, ctx.ts)) {
152
+ if (_chunk2A2QMA74cjs.isStrongBeat.call(void 0, pos, ctx.ts)) {
125
153
  ctx.prevMidi = emit(ctx, pos, rootPc);
126
154
  emitted = true;
127
155
  } else if (ctx.rng.prob(PICKUP_PROB)) {
@@ -140,7 +168,7 @@ function buildPop(ctx, seg) {
140
168
  }
141
169
  function buildArpeggio(ctx, seg) {
142
170
  const tones = chordTonePcs(seg.chord);
143
- const positions = beatPositions(seg.startBeat, seg.endBeat);
171
+ const positions = beatPositions(seg.startBeat, seg.endBeat, ctx.ts);
144
172
  positions.forEach((pos, i) => {
145
173
  const pc = _nullishCoalesce(tones[i % tones.length], () => ( bassPcOf(seg.chord)));
146
174
  ctx.prevMidi = emit(ctx, pos, pc);
@@ -152,7 +180,7 @@ function approachNote(ctx, target, from) {
152
180
  if (ctx.rng.prob(0.5)) {
153
181
  return foldIntoBand(chromatic, ctx.low);
154
182
  }
155
- const cand = _chunkFNDWXRRIcjs.nearestScaleTone.call(void 0, target + dir * 2, ctx.key);
183
+ const cand = _chunkBSLTLGM3cjs.nearestScaleTone.call(void 0, target + dir * 2, ctx.key);
156
184
  const step = Math.abs(cand - target);
157
185
  return foldIntoBand(step >= 1 && step <= 2 ? cand : chromatic, ctx.low);
158
186
  }
@@ -168,7 +196,7 @@ function foldIntoBand(midi, low) {
168
196
  }
169
197
  function buildWalking(ctx, seg, next) {
170
198
  const tones = chordTonePcs(seg.chord);
171
- const positions = beatPositions(seg.startBeat, seg.endBeat);
199
+ const positions = beatPositions(seg.startBeat, seg.endBeat, ctx.ts);
172
200
  const count = positions.length;
173
201
  positions.forEach((pos, i) => {
174
202
  if (i === 0) {
@@ -178,7 +206,7 @@ function buildWalking(ctx, seg, next) {
178
206
  if (next && count > 1 && i === count - 1) {
179
207
  const nextBass = placePc(bassPcOf(next.chord), ctx.prevMidi, ctx.low);
180
208
  const midi = approachNote(ctx, nextBass, ctx.prevMidi);
181
- ctx.prevMidi = emit(ctx, pos, _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, midi), midi);
209
+ ctx.prevMidi = emit(ctx, pos, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, midi), midi);
182
210
  return;
183
211
  }
184
212
  const pc = _nullishCoalesce(tones[i % tones.length], () => ( bassPcOf(seg.chord)));
@@ -186,36 +214,45 @@ function buildWalking(ctx, seg, next) {
186
214
  });
187
215
  }
188
216
  function generateBassLine(opts) {
189
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, opts.segments.length, "bass segments");
217
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, opts.segments.length, "bass segments");
190
218
  for (let index = 0; index < opts.segments.length; index += 1) {
191
219
  const segment = opts.segments[index];
192
220
  if (!segment) continue;
193
- _chunk7MGZ7D62cjs.assertRange.call(void 0, segment.startBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].startBeat`);
194
- _chunk7MGZ7D62cjs.assertRange.call(void 0, segment.endBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].endBeat`);
221
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, segment.startBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].startBeat`);
222
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, segment.endBeat, 0, Number.MAX_SAFE_INTEGER, `segments[${index}].endBeat`);
195
223
  if (segment.endBeat <= segment.startBeat) {
196
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`segments[${index}] must have a positive duration`);
224
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`segments[${index}] must have a positive duration`);
197
225
  }
198
226
  }
199
227
  const segments = [...opts.segments].sort((a, b) => a.startBeat - b.startBeat);
228
+ for (let index = 1; index < segments.length; index += 1) {
229
+ const previous = segments[index - 1];
230
+ const current = segments[index];
231
+ if (previous !== void 0 && current !== void 0 && current.startBeat < previous.endBeat) {
232
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
233
+ `bass segments must not overlap: segments ${index - 1} and ${index} overlap`
234
+ );
235
+ }
236
+ }
200
237
  if (segments.length === 0) {
201
238
  return [];
202
239
  }
203
240
  const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS));
204
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
205
- const style = _chunk7MGZ7D62cjs.assertOneOf.call(void 0, _nullishCoalesce(opts.style, () => ( DEFAULT_STYLE)), BASS_STYLES, "bass style");
241
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
242
+ const style = _chunkPI3UXRWKcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.style, () => ( DEFAULT_STYLE)), BASS_STYLES, "bass style");
206
243
  const octave = _nullishCoalesce(opts.octave, () => ( DEFAULT_OCTAVE));
207
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, octave, "bass octave", -1, 9);
244
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, octave, "bass octave", -1, 8);
208
245
  const estimatedNotes = segments.reduce(
209
246
  (count, segment) => count + Math.max(1, Math.ceil(segment.endBeat - segment.startBeat)),
210
247
  0
211
248
  );
212
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, estimatedNotes, "bass notes");
249
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, estimatedNotes, "bass notes");
213
250
  const low = octave * 12 + 12;
214
251
  const ctx = {
215
252
  ts,
216
253
  low,
217
254
  key: opts.key,
218
- rng: _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0))),
255
+ rng: _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0))),
219
256
  notes: [],
220
257
  prevMidi: low
221
258
  };
@@ -256,7 +293,7 @@ function generateBassLine(opts) {
256
293
  continue;
257
294
  }
258
295
  out.push({
259
- pitch: note.pitch,
296
+ pitch: _chunkPI3UXRWKcjs.clampToMidi.call(void 0, note.pitch, "generated bass pitch"),
260
297
  startBeat: note.startBeat,
261
298
  durationBeat,
262
299
  velocity: note.velocity
@@ -315,7 +352,7 @@ function complementOnsets(melody, ts, spanStart, spanEnd, rng) {
315
352
  place = isWholeBeat(beat);
316
353
  } else if (!attacked) {
317
354
  place = rng.prob(isWholeBeat(beat) ? HOLD_ON_BEAT_PROB : HOLD_OFF_BEAT_PROB);
318
- } else if (_chunk36N73KZ3cjs.isStrongBeat.call(void 0, beat, ts)) {
355
+ } else if (_chunk2A2QMA74cjs.isStrongBeat.call(void 0, beat, ts)) {
319
356
  place = rng.prob(REINFORCE_PROB);
320
357
  }
321
358
  if (place && beat - last >= MIN_ONSET_GAP - EPS2) {
@@ -337,7 +374,7 @@ function scoreCandidate(pitch, melPitch, melPrev, prevPitch, chord, center) {
337
374
  score -= 2;
338
375
  }
339
376
  }
340
- if (chord && _chunkFNDWXRRIcjs.chordToneRole.call(void 0, pitch, chord) !== null) {
377
+ if (chord && _chunkBSLTLGM3cjs.chordToneRole.call(void 0, pitch, chord) !== null) {
341
378
  score += 1;
342
379
  }
343
380
  if (prevPitch !== void 0) {
@@ -347,7 +384,7 @@ function scoreCandidate(pitch, melPitch, melPrev, prevPitch, chord, center) {
347
384
  if (dist > 0 && dist <= 2) {
348
385
  score += 1;
349
386
  }
350
- if (_chunkFNDWXRRIcjs.isForbiddenMelodicLeap.call(void 0, prevPitch, pitch)) {
387
+ if (_chunkBSLTLGM3cjs.isForbiddenMelodicLeap.call(void 0, prevPitch, pitch)) {
351
388
  score -= 6;
352
389
  }
353
390
  if (melPitch !== void 0 && melPrev !== void 0) {
@@ -388,7 +425,7 @@ function boundaryContexts(melody, harmony, startBeat, endBeat, ts) {
388
425
  melodyVoice,
389
426
  otherVoices: melodyVoice === void 0 ? [] : [melodyVoice],
390
427
  chord: harmony.chordAt(boundary),
391
- strongBeat: _chunk36N73KZ3cjs.isStrongBeat.call(void 0, boundary, ts),
428
+ strongBeat: _chunk2A2QMA74cjs.isStrongBeat.call(void 0, boundary, ts),
392
429
  atCounterOnset: Math.abs(boundary - startBeat) < EPS2
393
430
  };
394
431
  });
@@ -400,7 +437,7 @@ function heldPitchSafety(pitch, contexts, prevPitch, key, profile, register, low
400
437
  if (melodyVoice !== void 0 && (register === "below" && pitch >= melodyVoice.pitch || register === "above" && pitch <= melodyVoice.pitch)) {
401
438
  return null;
402
439
  }
403
- const result = _chunkQ6OP4U43cjs.evaluateSafety.call(void 0,
440
+ const result = _chunkWFDXOLUBcjs.evaluateSafety.call(void 0,
404
441
  {
405
442
  profile,
406
443
  candidatePitch: pitch,
@@ -429,42 +466,40 @@ function generateCounterMelody(opts) {
429
466
  }
430
467
  const chordChangeBeats = timeline === void 0 ? _nullishCoalesce(opts.chordChangeBeats, () => ( [])) : timeline.segments.flatMap((segment) => [segment.startBeat, segment.endBeat]);
431
468
  const register = _nullishCoalesce(opts.register, () => ( "below"));
432
- const melody = _chunk36N73KZ3cjs.createNoteEventIndex.call(void 0, opts.melody, {
433
- allowNonPositiveDuration: true,
469
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, opts.melody, "countermelody melody", { allowNonPositiveDuration: true });
470
+ const melody = _chunk2A2QMA74cjs.createNoteEventIndex.call(void 0, _chunkPI3UXRWKcjs.soundingNotesOnly.call(void 0, opts.melody), {
434
471
  tieBreak: register === "below" ? "highest" : "lowest"
435
472
  });
436
473
  const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS2));
437
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
438
- const rhythm = _chunk7MGZ7D62cjs.assertOneOf.call(void 0, _nullishCoalesce(opts.rhythm, () => ( "complement")), ["complement", "follow"], "rhythm");
439
- const profile = _chunk7MGZ7D62cjs.assertOneOf.call(void 0, _nullishCoalesce(opts.profile, () => ( "pop")), ["strict", "pop"], "safety profile");
440
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
441
- if (opts.pitchLow !== void 0) {
442
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, opts.pitchLow, "countermelody pitchLow");
443
- }
444
- if (opts.pitchHigh !== void 0) {
445
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, opts.pitchHigh, "countermelody pitchHigh");
446
- }
447
- if (melody.notes.every((indexed) => indexed.note.durationBeat <= 0)) {
474
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
475
+ const rhythm = _chunkPI3UXRWKcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.rhythm, () => ( "complement")), ["complement", "follow"], "rhythm");
476
+ const profile = _chunkPI3UXRWKcjs.assertOneOf.call(void 0, _nullishCoalesce(opts.profile, () => ( "pop")), ["strict", "pop"], "safety profile");
477
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
478
+ if (melody.notes.length === 0) {
448
479
  return [];
449
480
  }
450
481
  const spanStart = _nullishCoalesce(_optionalChain([melody, 'access', _8 => _8.notes, 'access', _9 => _9[0], 'optionalAccess', _10 => _10.note, 'access', _11 => _11.startBeat]), () => ( 0));
451
482
  const spanEnd = melody.notes.reduce((end, n) => Math.max(end, n.endBeat), spanStart);
452
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, Math.ceil((spanEnd - spanStart) / GRID_STEP), "countermelody grid");
483
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, Math.ceil((spanEnd - spanStart) / GRID_STEP), "countermelody grid");
453
484
  const meanPitch = melody.notes.reduce((sum, n) => sum + n.note.pitch, 0) / melody.notes.length;
454
485
  const defaultCenter = register === "below" ? meanPitch - REGISTER_OFFSET : meanPitch + REGISTER_OFFSET;
455
- const low = _nullishCoalesce(opts.pitchLow, () => ( Math.round(defaultCenter) - DEFAULT_HALF_RANGE));
456
- const high = _nullishCoalesce(opts.pitchHigh, () => ( Math.round(defaultCenter) + DEFAULT_HALF_RANGE));
457
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, low, "countermelody pitchLow");
458
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, high, "countermelody pitchHigh");
486
+ const low = _chunkPI3UXRWKcjs.clampToMidi.call(void 0,
487
+ _nullishCoalesce(opts.pitchLow, () => ( Math.round(defaultCenter) - DEFAULT_HALF_RANGE)),
488
+ "countermelody pitchLow"
489
+ );
490
+ const high = _chunkPI3UXRWKcjs.clampToMidi.call(void 0,
491
+ _nullishCoalesce(opts.pitchHigh, () => ( Math.round(defaultCenter) + DEFAULT_HALF_RANGE)),
492
+ "countermelody pitchHigh"
493
+ );
459
494
  if (low > high) {
460
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
495
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
461
496
  `countermelody pitchLow must not exceed pitchHigh; received ${low} > ${high}`
462
497
  );
463
498
  }
464
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, high - low + 1, "countermelody pitch candidates");
465
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, chordChangeBeats.length, "chord change beats");
499
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, high - low + 1, "countermelody pitch candidates");
500
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, chordChangeBeats.length, "chord change beats");
466
501
  for (let index = 0; index < chordChangeBeats.length; index += 1) {
467
- _chunk7MGZ7D62cjs.assertRange.call(void 0,
502
+ _chunkPI3UXRWKcjs.assertRange.call(void 0,
468
503
  _nullishCoalesce(chordChangeBeats[index], () => ( Number.NaN)),
469
504
  0,
470
505
  Number.MAX_SAFE_INTEGER,
@@ -474,7 +509,7 @@ function generateCounterMelody(opts) {
474
509
  const center = (low + high) / 2;
475
510
  const onsets = rhythm === "follow" ? followOnsets(melody) : complementOnsets(melody, ts, spanStart, spanEnd, rng);
476
511
  const boundaryEstimate = onsets.length + Math.ceil((spanEnd - spanStart) / GRID_STEP) + melody.notes.length + chordChangeBeats.length;
477
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, boundaryEstimate * (high - low + 1), "countermelody search");
512
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, boundaryEstimate * (high - low + 1), "countermelody search");
478
513
  const jitter = Array.from({ length: high - low + 1 }, () => rng.next() * TIE_BREAK_JITTER);
479
514
  const out = [];
480
515
  let prevPitch;
@@ -492,14 +527,14 @@ function generateCounterMelody(opts) {
492
527
  const contexts = boundaryContexts(melody, { chordAt, chordChangeBeats }, beat, endBeat, ts);
493
528
  const chord = _nullishCoalesce(_optionalChain([contexts, 'access', _14 => _14[0], 'optionalAccess', _15 => _15.chord]), () => ( chordAt(beat)));
494
529
  const otherVoices = melodyVoice !== void 0 ? [melodyVoice] : [];
495
- const candidates = _chunkQ6OP4U43cjs.enumerateSafePitches.call(void 0,
530
+ const candidates = _chunkWFDXOLUBcjs.enumerateSafePitches.call(void 0,
496
531
  {
497
532
  profile,
498
533
  prevPitch,
499
534
  chord,
500
535
  key: opts.key,
501
536
  otherVoices,
502
- strongBeat: _chunk36N73KZ3cjs.isStrongBeat.call(void 0, beat, ts),
537
+ strongBeat: _chunk2A2QMA74cjs.isStrongBeat.call(void 0, beat, ts),
503
538
  vocalLow: low,
504
539
  vocalHigh: high
505
540
  },
@@ -526,7 +561,7 @@ function generateCounterMelody(opts) {
526
561
  if (melPitch !== void 0) {
527
562
  if (prevPitch !== void 0 && melPrev !== void 0 && // Parallel octaves are the perfect-class-zero case of this predicate,
528
563
  // so a single check covers both parallel fifths and octaves.
529
- _chunkFNDWXRRIcjs.createsParallelPerfect.call(void 0, prevPitch, candidate, melPrev, melPitch)) {
564
+ _chunkBSLTLGM3cjs.createsParallelPerfect.call(void 0, prevPitch, candidate, melPrev, melPitch)) {
530
565
  continue;
531
566
  }
532
567
  }
@@ -547,7 +582,12 @@ function generateCounterMelody(opts) {
547
582
  1,
548
583
  Math.min(127, Math.round((_nullishCoalesce(_optionalChain([melNote, 'optionalAccess', _16 => _16.velocity]), () => ( DEFAULT_MELODY_VELOCITY))) - VELOCITY_DROP))
549
584
  );
550
- out.push({ pitch, startBeat: beat, durationBeat: endBeat - beat, velocity });
585
+ out.push({
586
+ pitch: _chunkPI3UXRWKcjs.clampToMidi.call(void 0, pitch),
587
+ startBeat: beat,
588
+ durationBeat: endBeat - beat,
589
+ velocity
590
+ });
551
591
  prevPitch = pitch;
552
592
  }
553
593
  return out;
@@ -819,13 +859,10 @@ function getGhostProbabilityAtPosition(beat, sixteenthInBeat) {
819
859
  const leadsIntoBackbeat = (beat === 0 || beat === 2) && sixteenthInBeat === 3;
820
860
  return leadsIntoBackbeat ? 0.6 : 0.25;
821
861
  }
822
- function selectGhostPositions(mood, rng) {
862
+ function selectGhostPositions(mood) {
823
863
  if (mood === 2 /* Energetic */) {
824
864
  return ["e", "a"];
825
865
  }
826
- if (mood === 0 /* Calm */) {
827
- return rng.prob(0.5) ? ["e"] : [];
828
- }
829
866
  return ["e"];
830
867
  }
831
868
 
@@ -841,6 +878,9 @@ function roleHiHatInstrument(role, useRide) {
841
878
  if (role === "ambient") {
842
879
  return GM.RIDE;
843
880
  }
881
+ if (role === "minimal") {
882
+ return GM.FHH;
883
+ }
844
884
  return useRide ? GM.RIDE : GM.CHH;
845
885
  }
846
886
  function sparser(level) {
@@ -1161,7 +1201,7 @@ function generateGhostNotesForBeat(ctx, sec) {
1161
1201
  if (ctx.beat !== 0 && ctx.beat !== 2) {
1162
1202
  return;
1163
1203
  }
1164
- const positions = selectGhostPositions(sec.ghostMood, ctx.rng);
1204
+ const positions = selectGhostPositions(sec.ghostMood);
1165
1205
  let ghostProb = getGhostDensity(sec.ghostMood, ctx.section, sec.backingDensity, ctx.bpm);
1166
1206
  if (sec.ghostBoost) {
1167
1207
  ghostProb = Math.min(1, ghostProb * 1.4);
@@ -1196,16 +1236,18 @@ function generatePreChorusBuildup(ctx, sec, isSectionLastBar) {
1196
1236
  const progress = (barInLift * 4 + ctx.beat) / (barsInLift * 4);
1197
1237
  const buildupVel = ctx.velocity * (0.5 + 0.5 * progress);
1198
1238
  ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, buildupVel);
1199
- ctx.track.add(GM.SD, ctx.beatTick + EIGHTH, EIGHTH, buildupVel * 0.85);
1239
+ ctx.track.add(
1240
+ GM.SD,
1241
+ swing16(ctx.beatTick + EIGHTH, sec, ctx.swingAmount),
1242
+ EIGHTH,
1243
+ buildupVel * 0.85
1244
+ );
1200
1245
  if (isSectionLastBar && ctx.beat === 3) {
1201
1246
  ctx.track.add(GM.CRASH, ctx.beatTick + EIGHTH + SIXTEENTH, SIXTEENTH, ctx.velocity * 1.1);
1202
1247
  }
1203
1248
  }
1204
1249
  function generateHiHatForBeat(ctx, sec) {
1205
1250
  if (!shouldPlayHiHat(sec.role)) {
1206
- if (sec.useFootHh && (ctx.beat === 0 || ctx.beat === 2)) {
1207
- ctx.track.add(GM.FHH, ctx.beatTick, EIGHTH, footHiHatVelocity(ctx.rng));
1208
- }
1209
1251
  return;
1210
1252
  }
1211
1253
  const crossStick = sec.useRide && shouldUseBridgeCrossStick(ctx.section, ctx.beat);
@@ -1213,11 +1255,12 @@ function generateHiHatForBeat(ctx, sec) {
1213
1255
  const hhType = sectionHiHatType(ctx.section, sec.role);
1214
1256
  const typeMult = hiHatTypeVelocityMultiplier(hhType);
1215
1257
  const isDynamicOpen = ctx.barHasOpenHh && ctx.beat === ctx.openHhBeat;
1258
+ const allowsOpenHiHat = sec.role !== "ambient" && sec.role !== "minimal";
1216
1259
  const dm = sec.densityMult;
1217
1260
  if (sec.hhLevel === "quarter") {
1218
1261
  const introRest = ctx.section === "intro" && ctx.beat !== 0;
1219
1262
  if (!introRest) {
1220
- if (isDynamicOpen) {
1263
+ if (isDynamicOpen && allowsOpenHiHat) {
1221
1264
  ctx.track.add(
1222
1265
  GM.OHH,
1223
1266
  ctx.beatTick,
@@ -1250,7 +1293,7 @@ function generateHiHatForBeat(ctx, sec) {
1250
1293
  continue;
1251
1294
  }
1252
1295
  const hhVel = Math.max(20, ctx.velocity * dm * typeMult * (eighth === 0 ? 0.9 : 0.65));
1253
- if (isDynamicOpen && eighth === 0) {
1296
+ if (isDynamicOpen && allowsOpenHiHat && eighth === 0) {
1254
1297
  ctx.track.add(GM.OHH, hhTick, EIGHTH, hhVel + OHH_VEL_BOOST);
1255
1298
  continue;
1256
1299
  }
@@ -1261,7 +1304,7 @@ function generateHiHatForBeat(ctx, sec) {
1261
1304
  } else if (eighth === 0) {
1262
1305
  useOpen = shouldAddOpenHHAccent(ctx.section, ctx.beat, ctx.bar, ctx.rng);
1263
1306
  }
1264
- if (useOpen) {
1307
+ if (useOpen && allowsOpenHiHat) {
1265
1308
  ctx.track.add(hiHatNote("open"), hhTick, EIGHTH, Math.max(20, hhVel * 1.1));
1266
1309
  } else {
1267
1310
  ctx.track.add(hhInstrument, hhTick, EIGHTH / 2, hhVel);
@@ -1276,11 +1319,11 @@ function generateHiHatForBeat(ctx, sec) {
1276
1319
  }
1277
1320
  const metricVel = hiHatVelocityMultiplier(sixteenth, ctx.rng);
1278
1321
  const hhVel = Math.max(20, ctx.velocity * dm * typeMult * metricVel);
1279
- if (isDynamicOpen && sixteenth === 0) {
1322
+ if (isDynamicOpen && allowsOpenHiHat && sixteenth === 0) {
1280
1323
  ctx.track.add(GM.OHH, hhTick, SIXTEENTH, hhVel + OHH_VEL_BOOST);
1281
1324
  continue;
1282
1325
  }
1283
- if (ctx.beat === 3 && sixteenth === 3) {
1326
+ if (allowsOpenHiHat && ctx.beat === 3 && sixteenth === 3) {
1284
1327
  const openProb = Math.max(0.1, Math.min(0.4, 30 / ctx.bpm));
1285
1328
  if (ctx.rng.prob(openProb)) {
1286
1329
  ctx.track.add(GM.OHH, hhTick, SIXTEENTH, Math.max(20, hhVel * 1.2));
@@ -1293,10 +1336,10 @@ function generateHiHatForBeat(ctx, sec) {
1293
1336
 
1294
1337
  // src/generate/drums/euclid.ts
1295
1338
  function euclideanRhythm(pulses, steps, rotation = 0) {
1296
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, steps, "euclidean steps");
1297
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, pulses, "euclidean pulses");
1298
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, rotation, "euclidean rotation");
1299
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, steps, "euclidean pattern steps");
1339
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, steps, "euclidean steps");
1340
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, pulses, "euclidean pulses");
1341
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, rotation, "euclidean rotation");
1342
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, steps, "euclidean pattern steps");
1300
1343
  const p = Math.max(0, Math.min(pulses, steps));
1301
1344
  const base = bjorklund(p, steps);
1302
1345
  const shift = (rotation % steps + steps) % steps;
@@ -1335,7 +1378,7 @@ function getFillStartBeat(energy) {
1335
1378
  return 2;
1336
1379
  case "high":
1337
1380
  case "peak":
1338
- return 0;
1381
+ return 2;
1339
1382
  }
1340
1383
  }
1341
1384
  function selectFillType(from, to, style, nextEnergy, rng) {
@@ -1600,7 +1643,7 @@ function generateFill(track, beatTick, beat, fillType, velocity) {
1600
1643
  }
1601
1644
 
1602
1645
  // src/generate/drums/hit.ts
1603
- var DRUM_NOTES = {
1646
+ var DRUM_NOTES = Object.freeze({
1604
1647
  kick: GM.BD,
1605
1648
  snare: GM.SD,
1606
1649
  sideStick: GM.SIDESTICK,
@@ -1615,7 +1658,7 @@ var DRUM_NOTES = {
1615
1658
  midTom: GM.TOM_M,
1616
1659
  lowTom: GM.TOM_L,
1617
1660
  shaker: GM.SHAKER
1618
- };
1661
+ });
1619
1662
  var VOICE_BY_NOTE = new Map(
1620
1663
  Object.entries(DRUM_NOTES).map(([voice, note]) => [note, voice])
1621
1664
  );
@@ -1794,12 +1837,12 @@ function getPercussionConfig(mood, section) {
1794
1837
  shaker16th: act[1]
1795
1838
  };
1796
1839
  }
1797
- function generateAuxPercussionForBar(track, barStart, config, role, densityMult, rng, bpm) {
1840
+ function generateAuxPercussionForBar(track, barStart, config, role, densityMult, rng, bpm, sec, swingAmount, barBeats = 4) {
1798
1841
  if (role === "minimal") {
1799
1842
  return;
1800
1843
  }
1801
1844
  if (config.tambourine) {
1802
- for (let beat = 1; beat <= 3; beat += 2) {
1845
+ for (let beat = 1; beat < barBeats; beat += 2) {
1803
1846
  const raw = 70 * densityMult * rng.float(0.9, 1.1);
1804
1847
  track.add(GM.TAMBOURINE, barStart + beat, EIGHTH, Math.max(40, Math.min(90, raw)));
1805
1848
  }
@@ -1808,12 +1851,12 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
1808
1851
  const use16th = config.shaker16th && bpm < HH_16TH_BPM_THRESHOLD;
1809
1852
  if (use16th) {
1810
1853
  const velCurve = [0.75, 0.45, 0.6, 0.45];
1811
- for (let beat = 0; beat < 4; beat += 1) {
1854
+ for (let beat = 0; beat < barBeats; beat += 1) {
1812
1855
  for (let sub = 0; sub < 4; sub += 1) {
1813
1856
  const raw = 80 * (_nullishCoalesce(velCurve[sub], () => ( 0.5))) * densityMult * rng.float(0.9, 1.1);
1814
1857
  track.add(
1815
1858
  GM.SHAKER,
1816
- barStart + beat + sub * SIXTEENTH,
1859
+ swing16(barStart + beat + sub * SIXTEENTH, sec, swingAmount),
1817
1860
  SIXTEENTH,
1818
1861
  Math.max(25, Math.min(85, raw))
1819
1862
  );
@@ -1821,12 +1864,12 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
1821
1864
  }
1822
1865
  } else {
1823
1866
  const velCurve = [0.75, 0.55];
1824
- for (let beat = 0; beat < 4; beat += 1) {
1867
+ for (let beat = 0; beat < barBeats; beat += 1) {
1825
1868
  for (let sub = 0; sub < 2; sub += 1) {
1826
1869
  const raw = 80 * (_nullishCoalesce(velCurve[sub], () => ( 0.6))) * densityMult * rng.float(0.9, 1.1);
1827
1870
  track.add(
1828
1871
  GM.SHAKER,
1829
- barStart + beat + sub * EIGHTH,
1872
+ swing16(barStart + beat + sub * EIGHTH, sec, swingAmount),
1830
1873
  EIGHTH,
1831
1874
  Math.max(25, Math.min(85, raw))
1832
1875
  );
@@ -1835,7 +1878,7 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
1835
1878
  }
1836
1879
  }
1837
1880
  if (config.handclap) {
1838
- for (let beat = 1; beat <= 3; beat += 2) {
1881
+ for (let beat = 1; beat < barBeats; beat += 2) {
1839
1882
  const raw = 85 * densityMult * rng.float(0.9, 1.1);
1840
1883
  track.add(GM.HANDCLAP, barStart + beat, EIGHTH, Math.max(50, Math.min(100, raw)));
1841
1884
  }
@@ -1845,27 +1888,30 @@ function generateAuxPercussionForBar(track, barStart, config, role, densityMult,
1845
1888
  // src/generate/drums/index.ts
1846
1889
  var MAX_HITS_PER_BAR = 128;
1847
1890
  function generateDrums(opts) {
1848
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, opts.bars, "drum bars");
1849
- _chunk7MGZ7D62cjs.assertRange.call(void 0, opts.bpm, Number.MIN_VALUE, 1e3, "drum bpm");
1850
- _chunk7MGZ7D62cjs.assertRange.call(void 0, opts.density, 0, 1, "drum density");
1851
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, opts.bars * MAX_HITS_PER_BAR, "drum hits");
1891
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, opts.bars, "drum bars");
1892
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, opts.bpm, Number.MIN_VALUE, 1e3, "drum bpm");
1893
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, opts.density, 0, 1, "drum density");
1894
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, opts.bars * MAX_HITS_PER_BAR, "drum hits");
1852
1895
  if (opts.euclideanKick) {
1853
1896
  const steps = _nullishCoalesce(opts.euclideanKick.steps, () => ( 16));
1854
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, steps, "euclidean steps", 1, 16);
1855
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, opts.euclideanKick.pulses, "euclidean pulses");
1856
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, _nullishCoalesce(opts.euclideanKick.rotation, () => ( 0)), "euclidean rotation");
1897
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, steps, "euclidean steps", 1, 16);
1898
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, opts.euclideanKick.pulses, "euclidean pulses");
1899
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, _nullishCoalesce(opts.euclideanKick.rotation, () => ( 0)), "euclidean rotation");
1857
1900
  }
1858
- const publicStyle = _chunk7MGZ7D62cjs.assertOneOf.call(void 0, opts.style, GROOVE_STYLES, "drum style");
1859
- const publicSection = _chunk7MGZ7D62cjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum section");
1901
+ const publicStyle = _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.style, GROOVE_STYLES, "drum style");
1902
+ const publicSection = _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.section, PUBLIC_SECTIONS, "drum section");
1860
1903
  if (opts.nextSection !== void 0) {
1861
- _chunk7MGZ7D62cjs.assertOneOf.call(void 0, opts.nextSection, PUBLIC_SECTIONS, "drum nextSection");
1904
+ _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.nextSection, PUBLIC_SECTIONS, "drum nextSection");
1862
1905
  }
1863
1906
  const track = new HitList();
1864
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
1907
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
1865
1908
  const mapping = mapStyle(publicStyle);
1866
1909
  const style = mapping.style;
1867
- const feel = opts.feel === void 0 ? mapping.feel : _chunk7MGZ7D62cjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "drum feel");
1868
- const role = opts.role === void 0 ? "full" : _chunk7MGZ7D62cjs.assertOneOf.call(void 0, opts.role, DRUM_ROLES, "drum role");
1910
+ const feel = opts.feel === void 0 ? mapping.feel : _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.feel, DRUM_FEELS, "drum feel");
1911
+ const role = opts.role === void 0 ? "full" : _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.role, DRUM_ROLES, "drum role");
1912
+ const ts = _nullishCoalesce(opts.ts, () => ( { numerator: 4, denominator: 4 }));
1913
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts, "drum time signature");
1914
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
1869
1915
  const playMainVoices = role !== "fxOnly";
1870
1916
  const section = mapSection(publicSection);
1871
1917
  const backingDensity = mapDensity(opts.density);
@@ -1901,7 +1947,7 @@ function generateDrums(opts) {
1901
1947
  const reuseSectionKick = (section === "b" || section === "chorus") && style !== "sparse";
1902
1948
  let sectionKick;
1903
1949
  for (let bar = 0; bar < opts.bars; bar += 1) {
1904
- const barStart = bar * 4;
1950
+ const barStart = bar * barBeats;
1905
1951
  const isLastBar = bar === opts.bars - 1;
1906
1952
  if (bar === 0 && section === "chorus") {
1907
1953
  track.add(GM.CRASH, barStart, 0.5, 100 * densityMult);
@@ -1921,7 +1967,7 @@ function generateDrums(opts) {
1921
1967
  }
1922
1968
  const inLift = isInPreChorusLift(section, bar, opts.bars, nextSection === "chorus");
1923
1969
  let currentFill = "snareRoll";
1924
- for (let beat = 0; beat < 4; beat += 1) {
1970
+ for (let beat = 0; beat < barBeats; beat += 1) {
1925
1971
  const beatTick = barStart + beat;
1926
1972
  const velocity = calculateVelocity(section, beat);
1927
1973
  if (opts.fills && isLastBar && !inLift && beat >= fillStartBeat) {
@@ -1958,14 +2004,14 @@ function generateDrums(opts) {
1958
2004
  generatePreChorusBuildup(ctx, sec, isLastBar);
1959
2005
  }
1960
2006
  if (euclidKick && !inLift) {
1961
- const stepLength = 4 / euclidSteps;
2007
+ const stepLength = barBeats / euclidSteps;
1962
2008
  for (let step = 0; step < euclidKick.length; step += 1) {
1963
2009
  const onset = step * stepLength;
1964
2010
  if (euclidKick[step] && Math.floor(onset) === beat) {
1965
2011
  const wholeBeat = Math.abs(onset - Math.round(onset)) < 1e-9;
1966
2012
  track.add(
1967
2013
  GM.BD,
1968
- barStart + onset,
2014
+ swing16(barStart + onset, sec, swingAmount),
1969
2015
  Math.min(0.5, stepLength),
1970
2016
  velocity * (wholeBeat ? 1 : 0.85)
1971
2017
  );
@@ -1982,7 +2028,7 @@ function generateDrums(opts) {
1982
2028
  generateHiHatForBeat(ctx, sec);
1983
2029
  }
1984
2030
  if (sec.useFootHh && shouldPlayHiHat(role)) {
1985
- for (let fhhBeat = 0; fhhBeat < 4; fhhBeat += 2) {
2031
+ for (let fhhBeat = 0; fhhBeat < barBeats; fhhBeat += 2) {
1986
2032
  const fhhTick = barStart + fhhBeat;
1987
2033
  if (!track.hasOnset(GM.FHH, fhhTick)) {
1988
2034
  track.add(GM.FHH, fhhTick, 0.5, footHiHatVelocity(rng));
@@ -2005,10 +2051,14 @@ function generateDrums(opts) {
2005
2051
  role,
2006
2052
  densityMult,
2007
2053
  rng,
2008
- opts.bpm
2054
+ opts.bpm,
2055
+ sec,
2056
+ swingAmount,
2057
+ barBeats
2009
2058
  );
2010
2059
  }
2011
- return [...track.hits].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
2060
+ const endBeat = opts.bars * barBeats;
2061
+ return track.hits.filter((hit) => hit.startBeat < endBeat).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
2012
2062
  }
2013
2063
 
2014
2064
  // src/generate/groove/index.ts
@@ -2023,22 +2073,22 @@ var MIN_VELOCITY = 1;
2023
2073
  var MAX_VELOCITY = 127;
2024
2074
  function humanize(events, opts = {}) {
2025
2075
  const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_TS3));
2026
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
2027
- _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, events, "humanize events", { allowNonPositiveDuration: true });
2028
- const sounding = _chunk7MGZ7D62cjs.dropSilentNotes.call(void 0, events);
2076
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
2077
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, events, "humanize events", { allowNonPositiveDuration: true });
2078
+ const sounding = _chunkPI3UXRWKcjs.dropSilentNotes.call(void 0, events);
2029
2079
  const timing = _nullishCoalesce(opts.timing, () => ( DEFAULT_TIMING));
2030
2080
  const velocityJitter = _nullishCoalesce(opts.velocity, () => ( DEFAULT_VELOCITY_JITTER));
2031
2081
  const accent = _nullishCoalesce(opts.accent, () => ( DEFAULT_ACCENT));
2032
2082
  const baseVelocity = _nullishCoalesce(opts.baseVelocity, () => ( DEFAULT_BASE_VELOCITY));
2033
- _chunk7MGZ7D62cjs.assertRange.call(void 0, timing, 0, Number.MAX_SAFE_INTEGER, "humanize timing");
2034
- _chunk7MGZ7D62cjs.assertRange.call(void 0, velocityJitter, 0, 127, "humanize velocity jitter");
2035
- _chunk7MGZ7D62cjs.assertRange.call(void 0, accent, 0, 127, "humanize accent");
2036
- _chunk7MGZ7D62cjs.assertRange.call(void 0, baseVelocity, 0, 127, "humanize base velocity");
2037
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2083
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, timing, 0, Number.MAX_SAFE_INTEGER, "humanize timing");
2084
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, velocityJitter, 0, 127, "humanize velocity jitter");
2085
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, accent, 0, 127, "humanize accent");
2086
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, baseVelocity, 0, 127, "humanize base velocity");
2087
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2038
2088
  return sounding.map((event) => {
2039
2089
  const timingOffset = rng.float(-timing, timing);
2040
2090
  const startBeat = Math.max(0, event.startBeat + timingOffset);
2041
- const weight = _chunk36N73KZ3cjs.metricWeight.call(void 0, event.startBeat, ts);
2091
+ const weight = _chunk2A2QMA74cjs.metricWeight.call(void 0, event.startBeat, ts);
2042
2092
  const accentBoost = weight / MAX_METRIC_WEIGHT * accent;
2043
2093
  const velocityOffset = rng.float(-velocityJitter, velocityJitter);
2044
2094
  const rawVelocity = (_nullishCoalesce(event.velocity, () => ( baseVelocity))) + accentBoost + velocityOffset;
@@ -2066,17 +2116,18 @@ function quantizeToGrid(beat, barBeats, subdivision, slotsPerBar) {
2066
2116
  };
2067
2117
  }
2068
2118
  function extractGrooveTemplate(events, ts, subdivision = DEFAULT_GROOVE_SUBDIVISION) {
2069
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
2070
- _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, events, "groove source events", { allowNonPositiveDuration: true });
2071
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, subdivision, "groove subdivision");
2072
- const barBeats = _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts);
2073
- const slotsPerBar = Math.round(barBeats * subdivision);
2074
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, slotsPerBar, "groove template slots");
2119
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
2120
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, events, "groove source events", { allowNonPositiveDuration: true });
2121
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, subdivision, "groove subdivision");
2122
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
2123
+ const slotsPerBar = Math.ceil(barBeats * subdivision);
2124
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, slotsPerBar, "groove template slots");
2125
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, slotsPerBar, "groove template slots");
2075
2126
  const offsetSums = new Array(slotsPerBar).fill(0);
2076
2127
  const velocitySums = new Array(slotsPerBar).fill(0);
2077
2128
  const counts = new Array(slotsPerBar).fill(0);
2078
2129
  const velocityCounts = new Array(slotsPerBar).fill(0);
2079
- for (const event of _chunk7MGZ7D62cjs.dropSilentNotes.call(void 0, events)) {
2130
+ for (const event of _chunkPI3UXRWKcjs.dropSilentNotes.call(void 0, events)) {
2080
2131
  const { quantizedBeat, slotIndex } = quantizeToGrid(
2081
2132
  event.startBeat,
2082
2133
  barBeats,
@@ -2106,21 +2157,21 @@ function extractGrooveTemplate(events, ts, subdivision = DEFAULT_GROOVE_SUBDIVIS
2106
2157
  return { subdivision, slotsPerBar, slots, ts };
2107
2158
  }
2108
2159
  function applyGrooveTemplate(events, template, ts) {
2109
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
2110
- _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, events, "groove target events", { allowNonPositiveDuration: true });
2111
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, template.subdivision, "template subdivision");
2112
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, template.slotsPerBar, "template slotsPerBar");
2113
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, template.slots.length, "template slots");
2160
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
2161
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, events, "groove target events", { allowNonPositiveDuration: true });
2162
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, template.subdivision, "template subdivision");
2163
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, template.slotsPerBar, "template slotsPerBar");
2164
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, template.slots.length, "template slots");
2114
2165
  if (template.slots.length !== template.slotsPerBar) {
2115
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("template slots length must equal slotsPerBar");
2166
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("template slots length must equal slotsPerBar");
2116
2167
  }
2117
2168
  if (template.ts && (template.ts.numerator !== ts.numerator || template.ts.denominator !== ts.denominator)) {
2118
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
2119
- `Groove template meter ${_chunk36N73KZ3cjs.formatTimeSignature.call(void 0, template.ts)} does not match apply-time meter ${_chunk36N73KZ3cjs.formatTimeSignature.call(void 0, ts)}`
2169
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
2170
+ `Groove template meter ${_chunk2A2QMA74cjs.formatTimeSignature.call(void 0, template.ts)} does not match apply-time meter ${_chunk2A2QMA74cjs.formatTimeSignature.call(void 0, ts)}`
2120
2171
  );
2121
2172
  }
2122
- const barBeats = _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts);
2123
- return _chunk7MGZ7D62cjs.dropSilentNotes.call(void 0, events).map((event) => {
2173
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
2174
+ return _chunkPI3UXRWKcjs.dropSilentNotes.call(void 0, events).map((event) => {
2124
2175
  const { quantizedBeat, slotIndex } = quantizeToGrid(
2125
2176
  event.startBeat,
2126
2177
  barBeats,
@@ -2128,7 +2179,7 @@ function applyGrooveTemplate(events, template, ts) {
2128
2179
  template.slotsPerBar
2129
2180
  );
2130
2181
  const slot = template.slots[slotIndex];
2131
- const startBeat = quantizedBeat + (_nullishCoalesce(_optionalChain([slot, 'optionalAccess', _21 => _21.timingOffset]), () => ( 0)));
2182
+ const startBeat = Math.max(0, quantizedBeat + (_nullishCoalesce(_optionalChain([slot, 'optionalAccess', _21 => _21.timingOffset]), () => ( 0))));
2132
2183
  const velocity = slot && slot.velocity !== null && slot.velocity !== void 0 ? Math.round(slot.velocity) : event.velocity;
2133
2184
  return {
2134
2185
  pitch: event.pitch,
@@ -2145,7 +2196,7 @@ var FALLBACK = {
2145
2196
  quality: "maj",
2146
2197
  secondaryDominant: false,
2147
2198
  base: 0,
2148
- pcs: _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, 0, "maj"))
2199
+ pcs: _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, 0, "maj"))
2149
2200
  };
2150
2201
  var DEFAULT_HARMONIC_RHYTHM = 2;
2151
2202
  var DEFAULT_PLACEMENT = { transposeSearch: false, octaveSearch: false };
@@ -2155,21 +2206,21 @@ var COMFORT_HIGH = 79;
2155
2206
  var TESSITURA_WEIGHT = 1e-3;
2156
2207
  var TIE_BREAK_JITTER2 = 1e-6;
2157
2208
  function inferKey(melody) {
2158
- let best = _chunkFNDWXRRIcjs.majorKey.call(void 0, 0);
2209
+ let best = _chunkBSLTLGM3cjs.majorKey.call(void 0, 0);
2159
2210
  let bestScore = Number.NEGATIVE_INFINITY;
2160
2211
  const candidates = [];
2161
2212
  for (let tonic = 0; tonic < 12; tonic += 1) {
2162
- candidates.push(_chunkFNDWXRRIcjs.majorKey.call(void 0, tonic), _chunkFNDWXRRIcjs.minorKey.call(void 0, tonic));
2213
+ candidates.push(_chunkBSLTLGM3cjs.majorKey.call(void 0, tonic), _chunkBSLTLGM3cjs.minorKey.call(void 0, tonic));
2163
2214
  }
2164
2215
  for (const key of candidates) {
2165
- const tonic = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
2166
- const isMinor = key.modeMask12 === _chunkFNDWXRRIcjs.NATURAL_MINOR_MASK;
2216
+ const tonic = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
2217
+ const isMinor = key.modeMask12 === _chunkBSLTLGM3cjs.NATURAL_MINOR_MASK;
2167
2218
  const leadingTone = (tonic + 11) % 12;
2168
2219
  let score = 0;
2169
2220
  for (const n of melody) {
2170
2221
  const w = Math.max(0.25, n.durationBeat);
2171
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, n.pitch);
2172
- if (_chunkFNDWXRRIcjs.isScaleTone.call(void 0, n.pitch, key) || isMinor && pc === leadingTone) {
2222
+ const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, n.pitch);
2223
+ if (_chunkBSLTLGM3cjs.isScaleTone.call(void 0, n.pitch, key) || isMinor && pc === leadingTone) {
2173
2224
  score += w;
2174
2225
  }
2175
2226
  if (pc === tonic) {
@@ -2184,16 +2235,16 @@ function inferKey(melody) {
2184
2235
  return best;
2185
2236
  }
2186
2237
  function buildCandidates(key, reharmonize) {
2187
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key);
2238
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key);
2188
2239
  const candidates = tones.map((rootPc, degree) => {
2189
- const quality = _chunkFNDWXRRIcjs.diatonicTriad.call(void 0, degree, key).quality;
2240
+ const quality = _chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, key).quality;
2190
2241
  return {
2191
2242
  rootPc,
2192
2243
  quality,
2193
2244
  degree,
2194
2245
  secondaryDominant: false,
2195
2246
  base: 0,
2196
- pcs: _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, rootPc, quality))
2247
+ pcs: _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, rootPc, quality))
2197
2248
  };
2198
2249
  });
2199
2250
  if (reharmonize !== "diatonic") {
@@ -2206,22 +2257,22 @@ function buildCandidates(key, reharmonize) {
2206
2257
  secondaryDominant: true,
2207
2258
  targetDegree: target,
2208
2259
  base: 1.5,
2209
- pcs: _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, rootPc, "dom7"))
2260
+ pcs: _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, rootPc, "dom7"))
2210
2261
  });
2211
2262
  }
2212
2263
  }
2213
2264
  if (reharmonize === "borrowed") {
2214
- const parallel = _chunkCAN76NCUcjs.parallelKey.call(void 0, key);
2215
- const parallelTones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, parallel);
2265
+ const parallel = _chunkKQN2ZYNDcjs.parallelKey.call(void 0, key);
2266
+ const parallelTones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, parallel);
2216
2267
  for (let degree = 0; degree < parallelTones.length; degree += 1) {
2217
- const chord = _chunkFNDWXRRIcjs.diatonicTriad.call(void 0, degree, parallel);
2218
- if (!_chunkCAN76NCUcjs.isDiatonic.call(void 0, chord, key)) {
2268
+ const chord = _chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, parallel);
2269
+ if (!_chunkKQN2ZYNDcjs.isDiatonic.call(void 0, chord, key)) {
2219
2270
  candidates.push({
2220
2271
  rootPc: chord.rootPc,
2221
2272
  quality: chord.quality,
2222
2273
  secondaryDominant: false,
2223
2274
  base: 1.25,
2224
- pcs: _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord)
2275
+ pcs: _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord)
2225
2276
  });
2226
2277
  }
2227
2278
  }
@@ -2250,12 +2301,12 @@ function emissionCost(seg, cand, melody, key, ts) {
2250
2301
  continue;
2251
2302
  }
2252
2303
  const w = Math.max(0.25, overlap);
2253
- if (pcs.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, note.pitch))) {
2304
+ if (pcs.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, note.pitch))) {
2254
2305
  continue;
2255
2306
  }
2256
- const strong = _chunk36N73KZ3cjs.isStrongBeat.call(void 0, overlapStart, ts);
2307
+ const strong = _chunk2A2QMA74cjs.isStrongBeat.call(void 0, overlapStart, ts);
2257
2308
  cost += (strong ? 10 : 2) * w;
2258
- if (!_chunkFNDWXRRIcjs.isScaleTone.call(void 0, note.pitch, key)) {
2309
+ if (!_chunkBSLTLGM3cjs.isScaleTone.call(void 0, note.pitch, key)) {
2259
2310
  cost += w;
2260
2311
  }
2261
2312
  }
@@ -2288,7 +2339,7 @@ function transitionCost(prev, cur, tonicPc) {
2288
2339
  return cost;
2289
2340
  }
2290
2341
  function harmonizeOnce(melody, key, candidates, segments, jitter, ts) {
2291
- const tonicPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
2342
+ const tonicPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
2292
2343
  const n = candidates.length;
2293
2344
  const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
2294
2345
  const seg0 = segments[0];
@@ -2338,43 +2389,49 @@ function harmonizeOnce(melody, key, candidates, segments, jitter, ts) {
2338
2389
  return { cost: bestCost, path };
2339
2390
  }
2340
2391
  function harmonizeMelody(opts) {
2341
- const noteIndex = _chunk36N73KZ3cjs.createNoteEventIndex.call(void 0, opts.melody, { allowNonPositiveDuration: true });
2392
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, opts.melody, "harmonize melody", { allowNonPositiveDuration: true });
2393
+ const soundingMelody = _chunkPI3UXRWKcjs.soundingNotesOnly.call(void 0, opts.melody);
2394
+ const noteIndex = _chunk2A2QMA74cjs.createNoteEventIndex.call(void 0, soundingMelody);
2342
2395
  const ts = _nullishCoalesce(opts.ts, () => ( DEFAULT_METER));
2343
- _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
2396
+ _chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
2344
2397
  const requestedKey = _nullishCoalesce(opts.key, () => ( "infer"));
2345
- const key = requestedKey === "infer" ? inferKey(opts.melody) : requestedKey;
2398
+ const key = requestedKey === "infer" ? inferKey(soundingMelody) : requestedKey;
2346
2399
  const placement = _nullishCoalesce(opts.placement, () => ( DEFAULT_PLACEMENT));
2347
- if (noteIndex.notes.every((indexed) => indexed.note.durationBeat <= 0)) {
2400
+ if (noteIndex.notes.length === 0) {
2348
2401
  return { transposeSemitones: 0, key, chords: [], melodyRoles: [] };
2349
2402
  }
2350
2403
  const candidates = buildCandidates(key, _nullishCoalesce(opts.reharmonize, () => ( "diatonic")));
2351
2404
  const candAt = (i) => _nullishCoalesce(candidates[i], () => ( FALLBACK));
2352
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2405
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2353
2406
  const jitter = candidates.map(() => rng.next() * TIE_BREAK_JITTER2);
2354
- const melodyEnd = noteIndex.notes.reduce((m, n) => Math.max(m, n.endBeat), 0);
2355
- const hr = _chunk7MGZ7D62cjs.assertRange.call(void 0,
2407
+ const melodyStart = noteIndex.notes.reduce(
2408
+ (start, indexed) => Math.min(start, indexed.note.startBeat),
2409
+ Number.POSITIVE_INFINITY
2410
+ );
2411
+ const melodyEnd = noteIndex.notes.reduce((end, indexed) => Math.max(end, indexed.endBeat), 0);
2412
+ const hr = _chunkPI3UXRWKcjs.assertRange.call(void 0,
2356
2413
  _nullishCoalesce(opts.harmonicRhythm, () => ( DEFAULT_HARMONIC_RHYTHM)),
2357
2414
  Number.MIN_VALUE,
2358
2415
  Number.MAX_SAFE_INTEGER,
2359
2416
  "harmonic rhythm"
2360
2417
  );
2361
- const segCount = Math.max(1, Math.ceil(melodyEnd / hr));
2362
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, segCount, "harmonic segments");
2363
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, segCount * candidates.length * candidates.length, "harmonization search");
2418
+ const segmentStart = Math.floor(melodyStart / hr) * hr;
2419
+ const segCount = Math.max(1, Math.ceil((melodyEnd - segmentStart) / hr));
2420
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, segCount, "harmonic segments");
2364
2421
  const segments = Array.from({ length: segCount }, (_, s) => ({
2365
- startBeat: s * hr,
2366
- endBeat: (s + 1) * hr,
2422
+ startBeat: segmentStart + s * hr,
2423
+ endBeat: segmentStart + (s + 1) * hr,
2367
2424
  noteIndices: []
2368
2425
  }));
2369
2426
  let memberships = 0;
2370
2427
  for (const indexed of noteIndex.notes) {
2371
- if (indexed.note.durationBeat <= 0) {
2372
- continue;
2373
- }
2374
- const first = Math.max(0, Math.floor(indexed.note.startBeat / hr));
2375
- const lastExclusive = Math.min(segCount, Math.ceil(indexed.endBeat / hr - Number.EPSILON));
2428
+ const first = Math.max(0, Math.floor((indexed.note.startBeat - segmentStart) / hr));
2429
+ const lastExclusive = Math.min(
2430
+ segCount,
2431
+ Math.ceil((indexed.endBeat - segmentStart) / hr - Number.EPSILON)
2432
+ );
2376
2433
  memberships += Math.max(0, lastExclusive - first);
2377
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, memberships, "note-to-segment memberships");
2434
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, memberships, "note-to-segment memberships");
2378
2435
  for (let segment = first; segment < lastExclusive; segment += 1) {
2379
2436
  _optionalChain([segments, 'access', _22 => _22[segment], 'optionalAccess', _23 => _23.noteIndices, 'access', _24 => _24.push, 'call', _25 => _25(indexed.originalIndex)]);
2380
2437
  }
@@ -2394,6 +2451,10 @@ function harmonizeMelody(opts) {
2394
2451
  }
2395
2452
  }
2396
2453
  }
2454
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0,
2455
+ transposes.length * segCount * candidates.length * candidates.length,
2456
+ "harmonization placement search"
2457
+ );
2397
2458
  let bestCost = Number.POSITIVE_INFINITY;
2398
2459
  let bestTs = 0;
2399
2460
  let bestPath = [];
@@ -2414,7 +2475,7 @@ function harmonizeMelody(opts) {
2414
2475
  const chord = {
2415
2476
  rootPc: cand.rootPc,
2416
2477
  quality: cand.quality,
2417
- startBeat: _nullishCoalesce(_optionalChain([segments, 'access', _26 => _26[s], 'optionalAccess', _27 => _27.startBeat]), () => ( s * hr))
2478
+ startBeat: _nullishCoalesce(_optionalChain([segments, 'access', _26 => _26[s], 'optionalAccess', _27 => _27.startBeat]), () => ( segmentStart + s * hr))
2418
2479
  };
2419
2480
  if (cand.degree !== void 0) {
2420
2481
  chord.degree = cand.degree;
@@ -2427,8 +2488,8 @@ function harmonizeMelody(opts) {
2427
2488
  const melodyRoles = bestMelody.map((note, noteIndex2) => {
2428
2489
  const segIdx = Math.min(segments.length - 1, Math.max(0, Math.floor(note.startBeat / hr)));
2429
2490
  const cand = candAt(_nullishCoalesce(bestPath[segIdx], () => ( 0)));
2430
- const chord = _chunkFNDWXRRIcjs.makeChord.call(void 0, cand.rootPc, cand.quality);
2431
- return { noteIndex: noteIndex2, role: _chunkFCANXBATcjs.roleOf.call(void 0, note.pitch, chord).role };
2491
+ const chord = _chunkBSLTLGM3cjs.makeChord.call(void 0, cand.rootPc, cand.quality);
2492
+ return { noteIndex: noteIndex2, role: _chunkYFJXS3AUcjs.roleOf.call(void 0, note.pitch, chord).role };
2432
2493
  });
2433
2494
  return { transposeSemitones: bestTs, key, chords, melodyRoles };
2434
2495
  }
@@ -2443,7 +2504,7 @@ function isDownbeat(startBeat, barBeats) {
2443
2504
  }
2444
2505
  function upScaleTone(pitch, key) {
2445
2506
  for (let d = 1; d <= 12; d += 1) {
2446
- if (_chunkFNDWXRRIcjs.isScaleTone.call(void 0, pitch + d, key)) {
2507
+ if (_chunkBSLTLGM3cjs.isScaleTone.call(void 0, pitch + d, key)) {
2447
2508
  return pitch + d;
2448
2509
  }
2449
2510
  }
@@ -2451,14 +2512,14 @@ function upScaleTone(pitch, key) {
2451
2512
  }
2452
2513
  function downScaleTone(pitch, key) {
2453
2514
  for (let d = 1; d <= 12; d += 1) {
2454
- if (_chunkFNDWXRRIcjs.isScaleTone.call(void 0, pitch - d, key)) {
2515
+ if (_chunkBSLTLGM3cjs.isScaleTone.call(void 0, pitch - d, key)) {
2455
2516
  return pitch - d;
2456
2517
  }
2457
2518
  }
2458
2519
  return pitch - 12;
2459
2520
  }
2460
2521
  function stepDiatonic(pitch, degrees, key) {
2461
- let p = _chunkFNDWXRRIcjs.nearestScaleTone.call(void 0, pitch, key);
2522
+ let p = _chunkBSLTLGM3cjs.nearestScaleTone.call(void 0, pitch, key);
2462
2523
  const steps = Math.abs(Math.trunc(degrees));
2463
2524
  for (let i = 0; i < steps; i += 1) {
2464
2525
  p = degrees >= 0 ? upScaleTone(p, key) : downScaleTone(p, key);
@@ -2466,12 +2527,12 @@ function stepDiatonic(pitch, degrees, key) {
2466
2527
  return p;
2467
2528
  }
2468
2529
  function nearestChordTone(pitch, chord) {
2469
- const pcs = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
2530
+ const pcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
2470
2531
  for (let d = 0; d <= 12; d += 1) {
2471
- if (pcs.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch - d))) {
2532
+ if (pcs.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch - d))) {
2472
2533
  return pitch - d;
2473
2534
  }
2474
- if (pcs.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch + d))) {
2535
+ if (pcs.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch + d))) {
2475
2536
  return pitch + d;
2476
2537
  }
2477
2538
  }
@@ -2524,16 +2585,16 @@ function contourOffsets(contour, count) {
2524
2585
  }
2525
2586
  function generateMotif(opts) {
2526
2587
  const contour = _nullishCoalesce(opts.contour, () => ( "arch"));
2527
- const bars = _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, opts.bars, "motif bars");
2528
- const barBeats = _chunk36N73KZ3cjs.beatsPerBar.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS4)));
2588
+ const bars = _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, opts.bars, "motif bars");
2589
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, _nullishCoalesce(opts.ts, () => ( DEFAULT_TS4)));
2529
2590
  const totalBeats = bars * barBeats;
2530
2591
  const noteCount = Math.max(3, bars * 2);
2531
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, noteCount, "motif notes");
2592
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, noteCount, "motif notes");
2532
2593
  const beatsPerNote = totalBeats / noteCount;
2533
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2534
- const requestedJitter = _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, _nullishCoalesce(opts.jitter, () => ( 0)), "motif jitter");
2594
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, _nullishCoalesce(opts.seed, () => ( 0)));
2595
+ const requestedJitter = _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(opts.jitter, () => ( 0)), "motif jitter");
2535
2596
  const jitterProb = Math.min(1, Math.max(0, requestedJitter));
2536
- const tonic = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, opts.key.rootPc) + 60;
2597
+ const tonic = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, opts.key.rootPc) + 60;
2537
2598
  const offsets = contourOffsets(contour, noteCount);
2538
2599
  const notes = [];
2539
2600
  for (let i = 0; i < noteCount; i += 1) {
@@ -2546,7 +2607,11 @@ function generateMotif(opts) {
2546
2607
  if (opts.chord && isDownbeat(startBeat, barBeats)) {
2547
2608
  pitch = nearestChordTone(pitch, opts.chord);
2548
2609
  }
2549
- notes.push({ pitch, startBeat, durationBeat: beatsPerNote });
2610
+ notes.push({
2611
+ pitch: _chunkPI3UXRWKcjs.clampToMidi.call(void 0, pitch, "generated motif pitch"),
2612
+ startBeat,
2613
+ durationBeat: beatsPerNote
2614
+ });
2550
2615
  }
2551
2616
  return { notes };
2552
2617
  }
@@ -2559,7 +2624,7 @@ function transformMotif(cell, t, amount, key) {
2559
2624
  function assertInRange(cell) {
2560
2625
  for (const note of cell.notes) {
2561
2626
  if (note.pitch < 0 || note.pitch > 127) {
2562
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
2627
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
2563
2628
  `transformed motif pitch ${note.pitch} is outside the MIDI range 0..127`
2564
2629
  );
2565
2630
  }
@@ -2567,15 +2632,18 @@ function assertInRange(cell) {
2567
2632
  return cell;
2568
2633
  }
2569
2634
  function transformUnchecked(cell, t, amount, key) {
2570
- _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
2635
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
2571
2636
  if (amount !== void 0) {
2572
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, amount, "motif transform amount");
2637
+ _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, amount, "motif transform amount");
2573
2638
  }
2574
- if ((t === "transposeDiatonic" || t === "sequence") && amount !== void 0) {
2575
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, amount, "diatonic transform amount");
2639
+ if ((t === "transposeChromatic" || t === "transposeDiatonic" || t === "sequence") && amount !== void 0) {
2640
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0,
2641
+ amount,
2642
+ `${t === "transposeChromatic" ? "chromatic" : "diatonic"} transform amount`
2643
+ );
2576
2644
  }
2577
2645
  if ((t === "augment" || t === "diminish") && amount !== void 0 && amount <= 0) {
2578
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("time transform amount must be positive");
2646
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("time transform amount must be positive");
2579
2647
  }
2580
2648
  const notes = cell.notes;
2581
2649
  switch (t) {
@@ -2636,10 +2704,10 @@ function scaleTime(cell, factor) {
2636
2704
  };
2637
2705
  }
2638
2706
  function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS4) {
2639
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, bars, "development bars");
2640
- _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
2707
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, bars, "development bars");
2708
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, cell.notes, "motif notes");
2641
2709
  const span = cellSpan(cell);
2642
- const totalBeats = bars * _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts);
2710
+ const totalBeats = bars * _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
2643
2711
  const origin = cellOrigin(cell.notes);
2644
2712
  const out = [];
2645
2713
  if (span <= 0) {
@@ -2647,7 +2715,7 @@ function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS4) {
2647
2715
  }
2648
2716
  const tileSpan = Math.max(span, MIN_TILE_SPAN);
2649
2717
  const tileCount = Math.ceil(totalBeats / tileSpan);
2650
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, tileCount * cell.notes.length, "developed motif notes");
2718
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, tileCount * cell.notes.length, "developed motif notes");
2651
2719
  for (let k = 0; k < tileCount; k += 1) {
2652
2720
  const offset = k * tileSpan;
2653
2721
  for (const n of cell.notes) {
@@ -2658,10 +2726,10 @@ function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS4) {
2658
2726
  const chord = timeline.at(startBeat);
2659
2727
  let pitch = n.pitch;
2660
2728
  if (chord) {
2661
- const isMember = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord).includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch));
2729
+ const isMember = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord).includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch));
2662
2730
  pitch = isMember ? pitch : nearestChordTone(pitch, chord);
2663
2731
  } else {
2664
- pitch = _chunkFNDWXRRIcjs.nearestScaleTone.call(void 0, pitch, key);
2732
+ pitch = _chunkBSLTLGM3cjs.nearestScaleTone.call(void 0, pitch, key);
2665
2733
  }
2666
2734
  out.push({ pitch, startBeat, durationBeat: n.durationBeat });
2667
2735
  }
@@ -2670,7 +2738,7 @@ function developMotif(cell, timeline, key, bars, ts = DEFAULT_TS4) {
2670
2738
  }
2671
2739
 
2672
2740
  // src/generate/progression/index.ts
2673
- var BORROWED_DEGREES = {
2741
+ var BORROWED_DEGREES = Object.freeze({
2674
2742
  /** Flat submediant: bVI. */
2675
2743
  bVI: 8,
2676
2744
  /** Flat subtonic: bVII. */
@@ -2683,7 +2751,7 @@ var BORROWED_DEGREES = {
2683
2751
  bII: 13,
2684
2752
  /** Sharp subdominant, the diminished #IV. */
2685
2753
  sharpIV: 14
2686
- };
2754
+ });
2687
2755
  var PRESETS = [
2688
2756
  {
2689
2757
  id: "fourChordPop",
@@ -2856,15 +2924,19 @@ function progressionsByStyle(style) {
2856
2924
  }
2857
2925
  function degreeToRootPc(degree, key) {
2858
2926
  if (degree >= 0 && degree <= 6) {
2859
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key);
2927
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key);
2860
2928
  return tones.length > 0 ? _nullishCoalesce(tones[degree % tones.length], () => ( key.rootPc % 12)) : key.rootPc % 12;
2861
2929
  }
2862
2930
  const offset = _nullishCoalesce(BORROWED_OFFSET[degree], () => ( 0));
2863
2931
  return ((key.rootPc % 12 + offset) % 12 + 12) % 12;
2864
2932
  }
2865
- function autoQuality(degree, key) {
2933
+ function autoQuality(degree, key, harmonicDominant) {
2866
2934
  if (degree >= 0 && degree <= 6) {
2867
- return _chunkFNDWXRRIcjs.diatonicTriad.call(void 0, degree, key).quality;
2935
+ const isMinor = (key.modeMask12 >> 3 & 1) === 1 && (key.modeMask12 >> 4 & 1) === 0;
2936
+ if (harmonicDominant && isMinor && degree === 4) {
2937
+ return "maj";
2938
+ }
2939
+ return _chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, key).quality;
2868
2940
  }
2869
2941
  if (degree === 14) {
2870
2942
  return "dim";
@@ -2874,13 +2946,13 @@ function autoQuality(degree, key) {
2874
2946
  }
2875
2947
  return "maj";
2876
2948
  }
2877
- function resolveCycle(degrees, key, ext) {
2949
+ function resolveCycle(degrees, key, ext, harmonicDominant) {
2878
2950
  const steps = [];
2879
2951
  for (const degree of degrees) {
2880
2952
  const step = {
2881
2953
  source: degree,
2882
2954
  rootPc: degreeToRootPc(degree, key),
2883
- quality: ext !== void 0 && ext !== "auto" ? ext : autoQuality(degree, key)
2955
+ quality: ext !== void 0 && ext !== "auto" ? ext : autoQuality(degree, key, harmonicDominant)
2884
2956
  };
2885
2957
  if (degree >= 0 && degree <= 6) {
2886
2958
  step.degree = degree;
@@ -2904,25 +2976,34 @@ function sameStep(a, b) {
2904
2976
  function pickProgressionPreset(style, seed = 0) {
2905
2977
  const pool = PRESETS.filter((preset) => preset.styles.includes(style));
2906
2978
  if (pool.length === 0) {
2907
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Unknown progression style: ${style}`);
2979
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Unknown progression style: ${style}`);
2908
2980
  }
2909
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, seed);
2981
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, seed);
2910
2982
  const index = Math.floor(rng.next() * pool.length) % pool.length;
2911
2983
  return _nullishCoalesce(pool[index], () => ( PRESETS[0]));
2912
2984
  }
2913
2985
  function generateProgression(opts) {
2914
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, opts.bars, "progression bars");
2915
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, opts.bars, "progression chords");
2986
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, opts.bars, "progression bars");
2987
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, opts.bars, "progression chords");
2916
2988
  const seed = _nullishCoalesce(opts.seed, () => ( 0));
2917
2989
  let preset;
2918
2990
  if (opts.preset !== void 0) {
2919
2991
  if (opts.preset.degrees.length === 0) {
2920
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("progression preset must name at least one degree");
2992
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("progression preset must name at least one degree");
2921
2993
  }
2922
2994
  preset = {
2923
2995
  id: _nullishCoalesce(opts.preset.id, () => ( "custom")),
2924
2996
  name: _nullishCoalesce(opts.preset.name, () => ( "Custom")),
2925
- degrees: opts.preset.degrees,
2997
+ degrees: opts.preset.degrees.map((degree, index) => {
2998
+ _chunkPI3UXRWKcjs.assertDegree.call(void 0, degree, `progression preset degrees[${index}]`);
2999
+ const supported = degree >= 0 && degree <= 6 || Object.values(BORROWED_DEGREES).includes(degree);
3000
+ if (!supported) {
3001
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
3002
+ `progression preset degrees[${index}] is not a supported progression degree; received ${degree}`
3003
+ );
3004
+ }
3005
+ return degree;
3006
+ }),
2926
3007
  functional: _nullishCoalesce(opts.preset.functional, () => ( "loop")),
2927
3008
  styles: _nullishCoalesce(opts.preset.styles, () => ( [opts.style]))
2928
3009
  };
@@ -2930,27 +3011,34 @@ function generateProgression(opts) {
2930
3011
  if (preset === void 0 && opts.presetId !== void 0) {
2931
3012
  preset = PRESETS.find((p) => p.id === opts.presetId);
2932
3013
  if (preset === void 0) {
2933
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Unknown progression preset: ${opts.presetId}`);
3014
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Unknown progression preset: ${opts.presetId}`);
2934
3015
  }
2935
3016
  }
2936
3017
  preset ??= pickProgressionPreset(opts.style, seed);
2937
- const cycle = resolveCycle(_nullishCoalesce(_optionalChain([preset, 'optionalAccess', _30 => _30.degrees]), () => ( [0])), opts.key, opts.ext);
3018
+ const ext = opts.ext === void 0 ? "auto" : _chunkPI3UXRWKcjs.assertOneOf.call(void 0, opts.ext, ["auto", ..._chunkBSLTLGM3cjs.chordQualities.call(void 0, )], "progression extension");
3019
+ const cycle = resolveCycle(
3020
+ _nullishCoalesce(_optionalChain([preset, 'optionalAccess', _30 => _30.degrees]), () => ( [0])),
3021
+ opts.key,
3022
+ ext,
3023
+ _optionalChain([preset, 'optionalAccess', _31 => _31.functional]) === "cadenceStrong"
3024
+ );
2938
3025
  const chords = [];
2939
3026
  for (let bar = 0; bar < opts.bars; bar += 1) {
2940
3027
  const step = cycle[bar % cycle.length];
2941
3028
  const chord = {
2942
- rootPc: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _31 => _31.rootPc]), () => ( 0)),
2943
- quality: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _32 => _32.quality]), () => ( "maj")),
3029
+ rootPc: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _32 => _32.rootPc]), () => ( 0)),
3030
+ quality: _nullishCoalesce(_optionalChain([step, 'optionalAccess', _33 => _33.quality]), () => ( "maj")),
2944
3031
  startBeat: bar * 4
2945
3032
  };
2946
- if (_optionalChain([step, 'optionalAccess', _33 => _33.degree]) !== void 0) {
3033
+ if (_optionalChain([step, 'optionalAccess', _34 => _34.degree]) !== void 0) {
2947
3034
  chord.degree = step.degree;
2948
3035
  }
2949
3036
  chords.push(chord);
2950
3037
  }
2951
3038
  if (opts.reharmonize) {
2952
3039
  const tonicPc = (opts.key.rootPc % 12 + 12) % 12;
2953
- const rrng = _chunkRUMU3UA6cjs.createRng.call(void 0, (seed ^ 2654435769) >>> 0);
3040
+ const rrng = _chunkMTMZTMWMcjs.createRng.call(void 0, (seed ^ 2654435769) >>> 0);
3041
+ let tonicStatements = chords.filter((chord) => chord.rootPc === tonicPc).length;
2954
3042
  for (let i = 0; i < chords.length - 1; i += 1) {
2955
3043
  const cur = chords[i];
2956
3044
  const next = chords[i + 1];
@@ -2959,12 +3047,16 @@ function generateProgression(opts) {
2959
3047
  }
2960
3048
  const domRoot = (next.rootPc + 7) % 12;
2961
3049
  const targetsTonic = (next.rootPc - tonicPc + 12) % 12 === 0;
3050
+ const isLastTonicStatement = cur.rootPc === tonicPc && tonicStatements <= 1;
2962
3051
  const alreadySecondary = cur.rootPc === domRoot && cur.quality === "dom7";
2963
- const isResolutionTarget = _optionalChain([chords, 'access', _34 => _34[i - 1], 'optionalAccess', _35 => _35.secondaryDominant]) === true;
2964
- if (targetsTonic || alreadySecondary || isResolutionTarget) {
3052
+ const isResolutionTarget = _optionalChain([chords, 'access', _35 => _35[i - 1], 'optionalAccess', _36 => _36.secondaryDominant]) === true;
3053
+ if (targetsTonic || isLastTonicStatement || alreadySecondary || isResolutionTarget) {
2965
3054
  continue;
2966
3055
  }
2967
3056
  if (rrng.next() < 0.5) {
3057
+ if (cur.rootPc === tonicPc) {
3058
+ tonicStatements -= 1;
3059
+ }
2968
3060
  chords[i] = {
2969
3061
  rootPc: domRoot,
2970
3062
  quality: "dom7",
@@ -2980,8 +3072,8 @@ function generateProgression(opts) {
2980
3072
  // src/generate/rhythm/index.ts
2981
3073
  var DEFAULT_RHYTHM_VELOCITY = 96;
2982
3074
  function rhythmToNoteEvents(events, pitch, velocity = DEFAULT_RHYTHM_VELOCITY) {
2983
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, pitch, "pitch");
2984
- _chunk7MGZ7D62cjs.assertRange.call(void 0, velocity, 0, 127, "velocity");
3075
+ _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, pitch, "pitch");
3076
+ _chunkPI3UXRWKcjs.assertRange.call(void 0, velocity, 0, 127, "velocity");
2985
3077
  return events.map((event) => ({
2986
3078
  pitch,
2987
3079
  startBeat: event.position,
@@ -3008,27 +3100,34 @@ function generateRhythm(ts, opts = {}) {
3008
3100
  const seed = _nullishCoalesce(opts.seed, () => ( 0));
3009
3101
  const bars = _nullishCoalesce(opts.bars, () => ( DEFAULT_BARS));
3010
3102
  const subdivision = _nullishCoalesce(opts.subdivision, () => ( DEFAULT_SUBDIVISION));
3011
- const density = _chunk7MGZ7D62cjs.assertRange.call(void 0, _nullishCoalesce(opts.density, () => ( DEFAULT_DENSITY)), 0, 1, "rhythm density");
3012
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, bars, "rhythm bars");
3013
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, subdivision, "rhythm subdivision");
3014
- const barBeats = _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts);
3103
+ const density = _chunkPI3UXRWKcjs.assertRange.call(void 0, _nullishCoalesce(opts.density, () => ( DEFAULT_DENSITY)), 0, 1, "rhythm density");
3104
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, bars, "rhythm bars");
3105
+ _chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, subdivision, "rhythm subdivision");
3106
+ const barBeats = _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
3015
3107
  const spanBeats = barBeats * bars;
3016
3108
  const step = 1 / subdivision;
3017
- const slotCount = Math.round(spanBeats * subdivision);
3018
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, slotCount, "rhythm grid slots");
3019
- const slotsPerBar = Math.round(barBeats * subdivision);
3020
- const rng = _chunkRUMU3UA6cjs.createRng.call(void 0, seed);
3109
+ const slotsPerBar = Math.ceil(barBeats * subdivision);
3110
+ const slotCount = slotsPerBar * bars;
3111
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, slotCount, "rhythm grid slots");
3112
+ const rng = _chunkMTMZTMWMcjs.createRng.call(void 0, seed);
3021
3113
  const positions = [];
3022
- for (let i = 0; i < slotCount; i += 1) {
3023
- const position = i * step;
3024
- if (i % slotsPerBar === 0) {
3025
- positions.push(position);
3026
- continue;
3027
- }
3028
- const weight = _chunk36N73KZ3cjs.metricWeight.call(void 0, position, ts);
3029
- const probability = onsetWeightCurve(weight) * density;
3030
- if (rng.prob(probability)) {
3031
- positions.push(position);
3114
+ for (let bar = 0; bar < bars; bar += 1) {
3115
+ const barStart = bar * barBeats;
3116
+ const barEnd = barStart + barBeats;
3117
+ for (let slot = 0; slot < slotsPerBar; slot += 1) {
3118
+ const position = barStart + slot * step;
3119
+ if (position >= barEnd - Number.EPSILON) {
3120
+ break;
3121
+ }
3122
+ if (slot === 0) {
3123
+ positions.push(position);
3124
+ continue;
3125
+ }
3126
+ const weight = _chunk2A2QMA74cjs.metricWeight.call(void 0, position, ts);
3127
+ const probability = onsetWeightCurve(weight) * density;
3128
+ if (rng.prob(probability)) {
3129
+ positions.push(position);
3130
+ }
3032
3131
  }
3033
3132
  }
3034
3133
  const events = [];
@@ -3044,8 +3143,8 @@ function rhythmDensity(events, ts) {
3044
3143
  return 0;
3045
3144
  }
3046
3145
  const last = events[events.length - 1];
3047
- const spanBeats = (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _36 => _36.position]), () => ( 0))) + (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _37 => _37.duration]), () => ( 0)));
3048
- const bars = spanBeats / _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts);
3146
+ const spanBeats = (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _37 => _37.position]), () => ( 0))) + (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _38 => _38.duration]), () => ( 0)));
3147
+ const bars = spanBeats / _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts);
3049
3148
  return bars > 0 ? events.length / bars : 0;
3050
3149
  }
3051
3150
 
@@ -3074,4 +3173,4 @@ function rhythmDensity(events, ts) {
3074
3173
 
3075
3174
 
3076
3175
  exports.BASS_STYLES = BASS_STYLES; exports.generateBassLine = generateBassLine; exports.generateCounterMelody = generateCounterMelody; exports.DRUM_NOTES = DRUM_NOTES; exports.drumVoiceOf = drumVoiceOf; exports.generateDrums = generateDrums; exports.humanize = humanize; exports.extractGrooveTemplate = extractGrooveTemplate; exports.applyGrooveTemplate = applyGrooveTemplate; exports.harmonizeMelody = harmonizeMelody; exports.generateMotif = generateMotif; exports.motifToNoteEvents = motifToNoteEvents; exports.transformMotif = transformMotif; exports.developMotif = developMotif; exports.BORROWED_DEGREES = BORROWED_DEGREES; exports.progressions = progressions; exports.progressionsByStyle = progressionsByStyle; exports.pickProgressionPreset = pickProgressionPreset; exports.generateProgression = generateProgression; exports.rhythmToNoteEvents = rhythmToNoteEvents; exports.onsetWeightCurve = onsetWeightCurve; exports.generateRhythm = generateRhythm; exports.rhythmDensity = rhythmDensity;
3077
- //# sourceMappingURL=chunk-4HLLZPXT.cjs.map
3176
+ //# sourceMappingURL=chunk-MPTVJIVG.cjs.map