@libraz/libcantus 1.1.0 → 1.3.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 (103) hide show
  1. package/README.md +4 -0
  2. package/README_ja.md +4 -0
  3. package/dist/analyze/index.cjs +31 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +155 -9
  6. package/dist/analyze/index.d.ts +155 -9
  7. package/dist/analyze/index.js +37 -13
  8. package/dist/{chunk-TT6PKMPR.cjs → chunk-35NBV2FT.cjs} +16 -16
  9. package/dist/{chunk-TT6PKMPR.cjs.map → chunk-35NBV2FT.cjs.map} +1 -1
  10. package/dist/{chunk-YPB3A3EY.cjs → chunk-3R3QVKY5.cjs} +314 -285
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-GDKE6RQW.cjs → chunk-4R2LWV7E.cjs} +691 -413
  13. package/dist/chunk-4R2LWV7E.cjs.map +1 -0
  14. package/dist/{chunk-RVS4G4MR.cjs → chunk-76ZBTRG2.cjs} +198 -198
  15. package/dist/{chunk-RVS4G4MR.cjs.map → chunk-76ZBTRG2.cjs.map} +1 -1
  16. package/dist/{chunk-X6RJAHII.js → chunk-7KG3RTYS.js} +2 -2
  17. package/dist/{chunk-PQL7E5F6.js → chunk-AGOVW7U6.js} +1 -1
  18. package/dist/{chunk-PQL7E5F6.js.map → chunk-AGOVW7U6.js.map} +1 -1
  19. package/dist/{chunk-5KW7DNCJ.cjs → chunk-B2PW6EHN.cjs} +2423 -453
  20. package/dist/chunk-B2PW6EHN.cjs.map +1 -0
  21. package/dist/{chunk-J772DL3B.cjs → chunk-CGBADIO5.cjs} +53 -53
  22. package/dist/{chunk-J772DL3B.cjs.map → chunk-CGBADIO5.cjs.map} +1 -1
  23. package/dist/{chunk-R5VUXQMP.js → chunk-CPMB7P75.js} +1978 -8
  24. package/dist/chunk-CPMB7P75.js.map +1 -0
  25. package/dist/{chunk-VLE5R7PF.js → chunk-GNVN4PPJ.js} +1874 -8
  26. package/dist/chunk-GNVN4PPJ.js.map +1 -0
  27. package/dist/{chunk-Q6R3QAZD.cjs → chunk-IFZDWIKM.cjs} +2199 -333
  28. package/dist/chunk-IFZDWIKM.cjs.map +1 -0
  29. package/dist/{chunk-DKICR7AN.js → chunk-IPEF6NWB.js} +296 -18
  30. package/dist/chunk-IPEF6NWB.js.map +1 -0
  31. package/dist/{chunk-AWEMD747.js → chunk-M5RYMAFI.js} +3 -3
  32. package/dist/{chunk-HSY2VQXZ.js → chunk-MLBPXA4Z.js} +330 -728
  33. package/dist/chunk-MLBPXA4Z.js.map +1 -0
  34. package/dist/{chunk-H66ARFU5.js → chunk-NB25P3EH.js} +3 -3
  35. package/dist/{chunk-5JJ6EHEZ.cjs → chunk-NM7UAMC3.cjs} +62 -62
  36. package/dist/{chunk-5JJ6EHEZ.cjs.map → chunk-NM7UAMC3.cjs.map} +1 -1
  37. package/dist/{chunk-R7B5N2JK.cjs → chunk-PM4XJE2Y.cjs} +47 -47
  38. package/dist/{chunk-R7B5N2JK.cjs.map → chunk-PM4XJE2Y.cjs.map} +1 -1
  39. package/dist/{chunk-7GOKQJTQ.js → chunk-PUKJP2Z5.js} +2 -2
  40. package/dist/{chunk-QCXYKSZS.cjs → chunk-PWC4CRWT.cjs} +19 -19
  41. package/dist/{chunk-QCXYKSZS.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
  42. package/dist/{chunk-FPBZFNCD.js → chunk-RFIBF2RR.js} +47 -18
  43. package/dist/{chunk-FPBZFNCD.js.map → chunk-RFIBF2RR.js.map} +1 -1
  44. package/dist/{chunk-XKSO2V5I.cjs → chunk-S5BKZZGM.cjs} +1 -1
  45. package/dist/{chunk-XKSO2V5I.cjs.map → chunk-S5BKZZGM.cjs.map} +1 -1
  46. package/dist/{chunk-HOHFYGKW.js → chunk-UMWZ4ZET.js} +2 -2
  47. package/dist/{chunk-XVMJO3QF.cjs → chunk-YFA7X2F6.cjs} +489 -887
  48. package/dist/chunk-YFA7X2F6.cjs.map +1 -0
  49. package/dist/{chunk-57WB2EDF.js → chunk-ZBUROTJQ.js} +3 -3
  50. package/dist/{coerce-wwdELNYS.d.ts → coerce-DP0vYu-x.d.ts} +77 -1
  51. package/dist/{coerce-CJlgULrQ.d.cts → coerce-DRERzdve.d.cts} +77 -1
  52. package/dist/core/index.cjs +5 -5
  53. package/dist/core/index.d.cts +2 -2
  54. package/dist/core/index.d.ts +2 -2
  55. package/dist/core/index.js +4 -4
  56. package/dist/generate/index.cjs +20 -8
  57. package/dist/generate/index.cjs.map +1 -1
  58. package/dist/generate/index.d.cts +87 -9
  59. package/dist/generate/index.d.ts +87 -9
  60. package/dist/generate/index.js +19 -7
  61. package/dist/{index-BaoKVVhE.d.cts → index-B9QLziDe.d.cts} +2 -2
  62. package/dist/{index-CnHQgqNh.d.ts → index-BHIUyjhZ.d.ts} +2 -2
  63. package/dist/{index-CBCP84rv.d.cts → index-BL5N2Oyz.d.cts} +1 -1
  64. package/dist/{index-D_hzxD8w.d.cts → index-BMweUlm5.d.cts} +3 -0
  65. package/dist/{index-Ci3xyows.d.ts → index-CL4b4gUQ.d.ts} +3 -0
  66. package/dist/{index-BzLNkmA7.d.cts → index-CY-3NVfy.d.cts} +486 -5
  67. package/dist/{index-CnIdP4EZ.d.ts → index-Dw2DP7vm.d.ts} +1 -1
  68. package/dist/{index-DpC-mDlV.d.ts → index-zaZdmnU4.d.ts} +486 -5
  69. package/dist/index.cjs +49 -13
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +10 -10
  72. package/dist/index.d.ts +10 -10
  73. package/dist/index.js +55 -19
  74. package/dist/{masks-O73Rhj5d.d.ts → masks-Cc7-MzzY.d.ts} +1 -1
  75. package/dist/{masks-C8TWLFIi.d.cts → masks-pADfTOiD.d.cts} +1 -1
  76. package/dist/model/index.cjs +12 -12
  77. package/dist/model/index.d.cts +213 -16
  78. package/dist/model/index.d.ts +213 -16
  79. package/dist/model/index.js +11 -11
  80. package/dist/{index-DTsKaPaC.d.cts → profile-CQMEqdH9.d.cts} +88 -601
  81. package/dist/{index-BUtIf2go.d.ts → profile-DLdlIpS9.d.ts} +88 -601
  82. package/dist/theory/index.cjs +5 -5
  83. package/dist/theory/index.d.cts +7 -7
  84. package/dist/theory/index.d.ts +7 -7
  85. package/dist/theory/index.js +4 -4
  86. package/dist/{index-BpWOhkkE.d.ts → types-D-XgxGvp.d.cts} +1170 -14
  87. package/dist/{index-B3WCj_Hm.d.cts → types-Dh7C064C.d.ts} +1170 -14
  88. package/package.json +6 -5
  89. package/dist/chunk-5KW7DNCJ.cjs.map +0 -1
  90. package/dist/chunk-DKICR7AN.js.map +0 -1
  91. package/dist/chunk-GDKE6RQW.cjs.map +0 -1
  92. package/dist/chunk-HSY2VQXZ.js.map +0 -1
  93. package/dist/chunk-Q6R3QAZD.cjs.map +0 -1
  94. package/dist/chunk-R5VUXQMP.js.map +0 -1
  95. package/dist/chunk-VLE5R7PF.js.map +0 -1
  96. package/dist/chunk-XVMJO3QF.cjs.map +0 -1
  97. package/dist/chunk-YPB3A3EY.cjs.map +0 -1
  98. /package/dist/{chunk-X6RJAHII.js.map → chunk-7KG3RTYS.js.map} +0 -0
  99. /package/dist/{chunk-AWEMD747.js.map → chunk-M5RYMAFI.js.map} +0 -0
  100. /package/dist/{chunk-H66ARFU5.js.map → chunk-NB25P3EH.js.map} +0 -0
  101. /package/dist/{chunk-7GOKQJTQ.js.map → chunk-PUKJP2Z5.js.map} +0 -0
  102. /package/dist/{chunk-HOHFYGKW.js.map → chunk-UMWZ4ZET.js.map} +0 -0
  103. /package/dist/{chunk-57WB2EDF.js.map → chunk-ZBUROTJQ.js.map} +0 -0
@@ -30,7 +30,6 @@
30
30
 
31
31
 
32
32
 
33
- var _chunkQ6R3QAZDcjs = require('./chunk-Q6R3QAZD.cjs');
34
33
 
35
34
 
36
35
 
@@ -41,7 +40,6 @@ var _chunkQ6R3QAZDcjs = require('./chunk-Q6R3QAZD.cjs');
41
40
 
42
41
 
43
42
 
44
- var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
45
43
 
46
44
 
47
45
 
@@ -54,6 +52,7 @@ var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
54
52
 
55
53
 
56
54
 
55
+ var _chunkIFZDWIKMcjs = require('./chunk-IFZDWIKM.cjs');
57
56
 
58
57
 
59
58
 
@@ -61,16 +60,34 @@ var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
61
60
 
62
61
 
63
62
 
63
+ var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
64
64
 
65
65
 
66
66
 
67
67
 
68
- var _chunkXKSO2V5Icjs = require('./chunk-XKSO2V5I.cjs');
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+ var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
69
86
 
70
87
  // src/analyze/arrange/internal.ts
71
88
  var MAX_LANE_LEAP = 24;
72
89
  function covers(note, beat) {
73
- return beat >= note.startBeat - _chunkXKSO2V5Icjs.BEAT_EPS && beat < note.endBeat - _chunkXKSO2V5Icjs.BEAT_EPS;
90
+ return beat >= note.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && beat < note.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS;
74
91
  }
75
92
  function isPercussion(role) {
76
93
  return role === "drums";
@@ -84,31 +101,31 @@ var TRACK_ROLE_TABLE = {
84
101
  };
85
102
  var TRACK_ROLES = Object.keys(TRACK_ROLE_TABLE);
86
103
  function trackRoleOf(role, name) {
87
- return role === void 0 ? "other" : _chunkXKSO2V5Icjs.assertOneOf.call(void 0, role, TRACK_ROLES, name);
104
+ return role === void 0 ? "other" : _chunkS5BKZZGMcjs.assertOneOf.call(void 0, role, TRACK_ROLES, name);
88
105
  }
89
106
  var DEFAULT_PROFILE = "pop";
90
- var SAFETY_PROFILES = Object.keys(_chunkYPB3A3EYcjs.PROFILE_WEIGHTS);
107
+ var SAFETY_PROFILES = Object.keys(_chunk3R3QVKY5cjs.PROFILE_WEIGHTS);
91
108
  function arrangementProfile(profile) {
92
- return _chunkXKSO2V5Icjs.assertOneOf.call(void 0, _nullishCoalesce(profile, () => ( DEFAULT_PROFILE)), SAFETY_PROFILES, "arrangement profile");
109
+ return _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(profile, () => ( DEFAULT_PROFILE)), SAFETY_PROFILES, "arrangement profile");
93
110
  }
94
111
  function assertTrackNotes(tracks, options) {
95
- _chunkXKSO2V5Icjs.assertArray.call(void 0, tracks, "arrangement tracks");
112
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, tracks, "arrangement tracks");
96
113
  let noteCount = 0;
97
114
  for (let index = 0; index < tracks.length; index += 1) {
98
115
  const notes = _optionalChain([tracks, 'access', _2 => _2[index], 'optionalAccess', _3 => _3.notes]);
99
116
  if (!Array.isArray(notes)) {
100
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
117
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
101
118
  `tracks[${index}].notes must be an array; received ${typeof notes}`
102
119
  );
103
120
  }
104
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, `tracks[${index}].notes`, options);
121
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, `tracks[${index}].notes`, options);
105
122
  noteCount += notes.length;
106
123
  }
107
124
  return noteCount;
108
125
  }
109
126
  function assertGivenTimeline(timeline, name) {
110
127
  if (timeline !== void 0) {
111
- _chunkQ6R3QAZDcjs.assertChordTimeline.call(void 0, timeline, name);
128
+ _chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, timeline, name);
112
129
  }
113
130
  }
114
131
  function assertGivenKeys(keys, name) {
@@ -116,25 +133,25 @@ function assertGivenKeys(keys, name) {
116
133
  return;
117
134
  }
118
135
  if (!Array.isArray(keys)) {
119
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
120
- `${name} must be an array of key regions; received ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, keys)}`
136
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
137
+ `${name} must be an array of key regions; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, keys)}`
121
138
  );
122
139
  }
123
140
  for (let index = 0; index < keys.length; index += 1) {
124
141
  const region = keys[index];
125
142
  if (typeof region !== "object" || region === null) {
126
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
127
- `${name}[${index}] must be a key region; received ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, region)}`
143
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
144
+ `${name}[${index}] must be a key region; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, region)}`
128
145
  );
129
146
  }
130
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, region.startBeat, `${name}[${index}].startBeat`);
131
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, region.endBeat, `${name}[${index}].endBeat`);
147
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, region.startBeat, `${name}[${index}].startBeat`);
148
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, region.endBeat, `${name}[${index}].endBeat`);
132
149
  if (region.key === void 0 || region.key === null || typeof region.key !== "string" && typeof region.key !== "object") {
133
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
134
- `${name}[${index}].key must name a key; received ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, region.key)}`
150
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
151
+ `${name}[${index}].key must name a key; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, region.key)}`
135
152
  );
136
153
  }
137
- _chunkYPB3A3EYcjs.resolveKey.call(void 0, region.key);
154
+ _chunk3R3QVKY5cjs.resolveKey.call(void 0, region.key);
138
155
  }
139
156
  }
140
157
  function harmonyTrackSet(harmonyTracks, tracks, name = "harmonyTracks") {
@@ -145,16 +162,16 @@ function harmonyTrackSet(harmonyTracks, tracks, name = "harmonyTracks") {
145
162
  for (let index = 0; index < harmonyTracks.length; index += 1) {
146
163
  const value = harmonyTracks[index];
147
164
  if (value === void 0 || !Number.isFinite(value)) {
148
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`${name}[${index}] must be a track index; received ${value}`);
165
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}[${index}] must be a track index; received ${value}`);
149
166
  }
150
167
  const track = Math.trunc(value);
151
168
  if (track < 0 || track >= tracks.length) {
152
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
169
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
153
170
  `${name}[${index}] ${track} is outside the arrangement's ${tracks.length} tracks`
154
171
  );
155
172
  }
156
173
  if (isPercussion(_optionalChain([tracks, 'access', _4 => _4[track], 'optionalAccess', _5 => _5.role]))) {
157
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
174
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
158
175
  `${name}[${index}] ${track} names a percussion track, whose pitches state no harmony`
159
176
  );
160
177
  }
@@ -195,7 +212,7 @@ function splitIntoSubVoices(ordered) {
195
212
  continue;
196
213
  }
197
214
  const end = last.startBeat + last.durationBeat;
198
- if (!_chunkQ6R3QAZDcjs.hasEnded.call(void 0, end, note.startBeat)) {
215
+ if (!_chunkIFZDWIKMcjs.hasEnded.call(void 0, end, note.startBeat)) {
199
216
  continue;
200
217
  }
201
218
  const distance = Math.abs(last.pitch - note.pitch);
@@ -216,13 +233,13 @@ function splitIntoSubVoices(ordered) {
216
233
  for (let start = 0; start < ordered.length; ) {
217
234
  const onset = _nullishCoalesce(_optionalChain([ordered, 'access', _13 => _13[start], 'optionalAccess', _14 => _14.startBeat]), () => ( 0));
218
235
  let end = start + 1;
219
- while (end < ordered.length && _chunkQ6R3QAZDcjs.sameInstant.call(void 0, _nullishCoalesce(_optionalChain([ordered, 'access', _15 => _15[end], 'optionalAccess', _16 => _16.startBeat]), () => ( 0)), onset)) {
236
+ while (end < ordered.length && _chunkIFZDWIKMcjs.sameInstant.call(void 0, _nullishCoalesce(_optionalChain([ordered, 'access', _15 => _15[end], 'optionalAccess', _16 => _16.startBeat]), () => ( 0)), onset)) {
220
237
  end += 1;
221
238
  }
222
239
  const block = ordered.slice(start, end);
223
240
  if (block.length > 1) {
224
241
  const freeLanes = lanes.map((lane, index) => ({ index, last: lane[lane.length - 1] })).filter(
225
- (entry) => entry.last !== void 0 && _chunkQ6R3QAZDcjs.hasEnded.call(void 0, entry.last.startBeat + entry.last.durationBeat, onset)
242
+ (entry) => entry.last !== void 0 && _chunkIFZDWIKMcjs.hasEnded.call(void 0, entry.last.startBeat + entry.last.durationBeat, onset)
226
243
  ).sort((a, b) => a.last.pitch - b.last.pitch);
227
244
  const assignment = pairWithFreeLanes(block, freeLanes);
228
245
  const taken = new Set(
@@ -310,7 +327,7 @@ function lastStartingAtOrBefore(sounding, beat) {
310
327
  let high = sounding.length;
311
328
  while (low < high) {
312
329
  const middle = Math.floor((low + high) / 2);
313
- if ((_nullishCoalesce(_optionalChain([sounding, 'access', _22 => _22[middle], 'optionalAccess', _23 => _23.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkXKSO2V5Icjs.BEAT_EPS) {
330
+ if ((_nullishCoalesce(_optionalChain([sounding, 'access', _22 => _22[middle], 'optionalAccess', _23 => _23.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkS5BKZZGMcjs.BEAT_EPS) {
314
331
  low = middle + 1;
315
332
  } else {
316
333
  high = middle;
@@ -326,8 +343,10 @@ function prepareTracks(tracks) {
326
343
  const sounding = voice.map((note, i) => {
327
344
  const prev = i > 0 ? voice[i - 1] : void 0;
328
345
  const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
329
- const contiguous = prevEnd !== void 0 && _chunkQ6R3QAZDcjs.adjacent.call(void 0, prevEnd, note.startBeat);
346
+ const contiguous = prevEnd !== void 0 && _chunkIFZDWIKMcjs.adjacent.call(void 0, prevEnd, note.startBeat);
330
347
  return {
348
+ id: note.id,
349
+ originalIndex: note.originalIndex,
331
350
  pitch: note.pitch,
332
351
  prevPitch: contiguous ? _optionalChain([prev, 'optionalAccess', _24 => _24.pitch]) : void 0,
333
352
  startBeat: note.startBeat,
@@ -357,7 +376,7 @@ function withGivenKeys(result, given) {
357
376
  if (given === void 0) {
358
377
  return result;
359
378
  }
360
- return { ...result, keys: given, prevailingKey: _nullishCoalesce(_chunkQ6R3QAZDcjs.prevailingKeyOf.call(void 0, given), () => ( result.prevailingKey)) };
379
+ return { ...result, keys: given, prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, given), () => ( result.prevailingKey)) };
361
380
  }
362
381
  function callerTimeline(timeline, key, given, pooled, meters, budget) {
363
382
  const chartEnd = timeline.segments.reduce((end, segment) => Math.max(end, segment.endBeat), 0);
@@ -366,15 +385,15 @@ function callerTimeline(timeline, key, given, pooled, meters, budget) {
366
385
  0
367
386
  );
368
387
  const totalBeats = Math.max(chartEnd, soundingEnd);
369
- const { startBeat: musicStart } = _chunkQ6R3QAZDcjs.gridForNotes.call(void 0, pooled, _chunkXKSO2V5Icjs.beatsPerBarAt.call(void 0, 0, meters));
370
- const keys = _nullishCoalesce(given, () => ( _chunkQ6R3QAZDcjs.attachPivots.call(void 0,
371
- key !== void 0 ? [{ startBeat: musicStart, endBeat: totalBeats, key: _chunkYPB3A3EYcjs.resolveKey.call(void 0, key), confidence: 1 }] : _chunkQ6R3QAZDcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }),
388
+ const { startBeat: musicStart } = _chunkIFZDWIKMcjs.gridForNotes.call(void 0, pooled, _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters));
389
+ const keys = _nullishCoalesce(given, () => ( _chunkIFZDWIKMcjs.attachPivots.call(void 0,
390
+ key !== void 0 ? [{ startBeat: musicStart, endBeat: totalBeats, key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, key), confidence: 1 }] : _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }),
372
391
  timeline.segments
373
392
  )));
374
393
  return {
375
394
  timeline,
376
395
  keys,
377
- prevailingKey: _nullishCoalesce(_chunkQ6R3QAZDcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunkYPB3A3EYcjs.resolveKey.call(void 0, _chunkYPB3A3EYcjs.majorKey.call(void 0, 0)))),
396
+ prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))),
378
397
  segmentConfidence: timeline.segments.map(() => 0)
379
398
  };
380
399
  }
@@ -391,11 +410,11 @@ function soundingVoicesAt(prepared, beat) {
391
410
  continue;
392
411
  }
393
412
  const snap = { pitch: note.pitch };
394
- const previous = _chunkQ6R3QAZDcjs.sameInstant.call(void 0, note.startBeat, beat) ? note.prevPitch : note.pitch;
413
+ const previous = _chunkIFZDWIKMcjs.sameInstant.call(void 0, note.startBeat, beat) ? note.prevPitch : note.pitch;
395
414
  if (previous !== void 0) {
396
415
  snap.prevPitch = previous;
397
416
  }
398
- out.push({ track: t, voice: v, snapshot: snap });
417
+ out.push({ track: t, voice: v, trackIndex: track.trackIndex, note, snapshot: snap });
399
418
  }
400
419
  }
401
420
  return out;
@@ -403,14 +422,28 @@ function soundingVoicesAt(prepared, beat) {
403
422
  function otherVoicesSounding(prepared, excludeTrack, excludeVoice, beat, cache) {
404
423
  const sounding = _nullishCoalesce(cache.get(beat), () => ( soundingVoicesAt(prepared, beat)));
405
424
  cache.set(beat, sounding);
406
- return sounding.filter(({ track, voice }) => track !== excludeTrack || voice !== excludeVoice).map(({ snapshot }) => snapshot);
425
+ return sounding.filter(({ track, voice }) => track !== excludeTrack || voice !== excludeVoice);
426
+ }
427
+ function conflictPartner(sounding, reasons) {
428
+ const partner = {
429
+ trackIndex: sounding.trackIndex,
430
+ noteId: sounding.note.id,
431
+ reasons
432
+ };
433
+ if (sounding.note.originalIndex !== void 0) {
434
+ partner.originalIndex = sounding.note.originalIndex;
435
+ }
436
+ return partner;
437
+ }
438
+ function snapshotsOf(voices) {
439
+ return voices.map(({ snapshot }) => snapshot);
407
440
  }
408
441
  function analyzePolyphony(notes, chordAtBeat, key) {
409
- const passage = _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, "notes", { allowNonPositiveDuration: true });
410
- _chunkXKSO2V5Icjs.assertFunction.call(void 0, chordAtBeat, "chordAtBeat");
442
+ const passage = _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "notes", { allowNonPositiveDuration: true });
443
+ _chunkS5BKZZGMcjs.assertFunction.call(void 0, chordAtBeat, "chordAtBeat");
411
444
  const prepared = prepareTracks([{ notes: [...passage] }]);
412
445
  const track = prepared[0];
413
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
446
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
414
447
  passage.length * (_nullishCoalesce(_optionalChain([track, 'optionalAccess', _29 => _29.voices, 'access', _30 => _30.length]), () => ( 0))),
415
448
  "polyphony note-voice comparisons"
416
449
  );
@@ -421,11 +454,11 @@ function analyzePolyphony(notes, chordAtBeat, key) {
421
454
  if (subVoice === void 0) {
422
455
  continue;
423
456
  }
424
- const analyzed = _chunkQ6R3QAZDcjs.analyzeVoice.call(void 0,
457
+ const analyzed = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0,
425
458
  subVoice.voice,
426
459
  chordAtBeat,
427
460
  key,
428
- (beat) => otherVoicesSounding(prepared, 0, v, beat, soundingCache)
461
+ (beat) => snapshotsOf(otherVoicesSounding(prepared, 0, v, beat, soundingCache))
429
462
  );
430
463
  for (const note of analyzed) {
431
464
  if (note.originalIndex !== void 0) {
@@ -439,7 +472,7 @@ function analyzePolyphony(notes, chordAtBeat, key) {
439
472
  if (analyzed !== void 0) {
440
473
  return analyzed;
441
474
  }
442
- const [only] = _chunkQ6R3QAZDcjs.analyzeVoice.call(void 0, [{ ...note, id: index }], chordAtBeat, key);
475
+ const [only] = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0, [{ ...note, id: index }], chordAtBeat, key);
443
476
  return _nullishCoalesce(only, () => ( {
444
477
  noteId: index,
445
478
  pitch: note.pitch,
@@ -454,7 +487,7 @@ function firstSegmentAfter(timeline, beat) {
454
487
  let high = timeline.segments.length;
455
488
  while (low < high) {
456
489
  const middle = Math.floor((low + high) / 2);
457
- if ((_nullishCoalesce(_optionalChain([timeline, 'access', _35 => _35.segments, 'access', _36 => _36[middle], 'optionalAccess', _37 => _37.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkXKSO2V5Icjs.BEAT_EPS) {
490
+ if ((_nullishCoalesce(_optionalChain([timeline, 'access', _35 => _35.segments, 'access', _36 => _36[middle], 'optionalAccess', _37 => _37.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkS5BKZZGMcjs.BEAT_EPS) {
458
491
  low = middle + 1;
459
492
  } else {
460
493
  high = middle;
@@ -467,7 +500,7 @@ function evaluationBeats(note, timeline) {
467
500
  const beats = [note.startBeat];
468
501
  for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {
469
502
  const start = _optionalChain([timeline, 'access', _38 => _38.segments, 'access', _39 => _39[i], 'optionalAccess', _40 => _40.startBeat]);
470
- if (start === void 0 || start >= noteEnd - _chunkXKSO2V5Icjs.BEAT_EPS) {
503
+ if (start === void 0 || start >= noteEnd - _chunkS5BKZZGMcjs.BEAT_EPS) {
471
504
  break;
472
505
  }
473
506
  beats.push(start);
@@ -478,19 +511,19 @@ function analyzeArrangement(tracks, opts = {}) {
478
511
  return analyzeArrangementWith(tracks, opts).analysis;
479
512
  }
480
513
  function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
481
- const meters = _chunkXKSO2V5Icjs.resolveMeters.call(void 0, opts, "arrangement meters");
514
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "arrangement meters");
482
515
  const budget = opts.budget;
483
516
  const noteOptions = { allowNonPositiveDuration: true, budget };
484
517
  if (opts.pickupBeats !== void 0) {
485
- _chunkXKSO2V5Icjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
518
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
486
519
  noteOptions.minStartBeat = -opts.pickupBeats;
487
520
  }
488
- _chunkXKSO2V5Icjs.assertArray.call(void 0, tracks, "arrangement tracks");
489
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
521
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, tracks, "arrangement tracks");
522
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
490
523
  const noteCount = assertTrackNotes(tracks, noteOptions);
491
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
524
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
492
525
  const profile = arrangementProfile(opts.profile);
493
- const minSeverity = opts.minSeverity === void 0 ? 1 /* Warning */ : _chunkXKSO2V5Icjs.assertInteger.call(void 0,
526
+ const minSeverity = opts.minSeverity === void 0 ? 1 /* Warning */ : _chunkS5BKZZGMcjs.assertInteger.call(void 0,
494
527
  opts.minSeverity,
495
528
  "arrangement minSeverity",
496
529
  0 /* Safe */,
@@ -501,12 +534,12 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
501
534
  const harmonyTracks = harmonyTrackSet(opts.harmonyTracks, tracks);
502
535
  const prepared = prepareTracks(tracks);
503
536
  const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
504
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, noteCount * voiceCount, "arrangement note-voice comparisons", budget);
537
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, noteCount * voiceCount, "arrangement note-voice comparisons", budget);
505
538
  const pooled = poolNotes(tracks, harmonyTracks);
506
539
  let evidence;
507
540
  let inferred;
508
541
  if (opts.timeline === void 0) {
509
- const run = _chunkQ6R3QAZDcjs.analyzeTimeline.call(void 0,
542
+ const run = _chunkIFZDWIKMcjs.analyzeTimeline.call(void 0,
510
543
  pooled,
511
544
  {
512
545
  key: opts.key,
@@ -524,9 +557,9 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
524
557
  inferred = callerTimeline(opts.timeline, opts.key, opts.keys, pooled, meters, budget);
525
558
  }
526
559
  const { timeline, keys, prevailingKey, segmentConfidence } = inferred;
527
- const keyAt = _chunkQ6R3QAZDcjs.keyLookup.call(void 0, keys, prevailingKey);
528
- const scaleAt = (beat) => _chunkYPB3A3EYcjs.scaleOf.call(void 0, keyAt(beat));
529
- const cadences = _chunkQ6R3QAZDcjs.detectCadences.call(void 0, timeline, scaleAt);
560
+ const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, keys, prevailingKey);
561
+ const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
562
+ const cadences = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, scaleAt);
530
563
  const soundingCache = /* @__PURE__ */ new Map();
531
564
  const trackAnalyses = [];
532
565
  const conflicts = [];
@@ -545,11 +578,11 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
545
578
  if (!subVoice) {
546
579
  continue;
547
580
  }
548
- const analyzed = _chunkQ6R3QAZDcjs.analyzeVoice.call(void 0,
581
+ const analyzed = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0,
549
582
  subVoice.voice,
550
583
  timeline.at,
551
584
  scaleAt,
552
- (beat) => otherVoicesSounding(prepared, t, v, beat, soundingCache)
585
+ (beat) => snapshotsOf(otherVoicesSounding(prepared, t, v, beat, soundingCache))
553
586
  );
554
587
  for (const note of analyzed) {
555
588
  notes.push({ ...note, trackIndex: track.trackIndex });
@@ -562,19 +595,20 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
562
595
  continue;
563
596
  }
564
597
  for (const beat of evaluationBeats(note, timeline)) {
565
- const atOnset = _chunkQ6R3QAZDcjs.sameInstant.call(void 0, beat, note.startBeat);
598
+ const atOnset = _chunkIFZDWIKMcjs.sameInstant.call(void 0, beat, note.startBeat);
599
+ const others = otherVoicesSounding(prepared, t, v, beat, soundingCache);
566
600
  const safetyQuery = {
567
601
  profile,
568
602
  candidatePitch: note.pitch,
569
603
  prevPitch: atOnset ? preparedNote.prevPitch : note.pitch,
570
604
  chord: timeline.at(beat),
571
605
  key: scaleAt(beat),
572
- otherVoices: otherVoicesSounding(prepared, t, v, beat, soundingCache),
573
- strongBeat: _chunkXKSO2V5Icjs.isStrongBeat.call(void 0, beat, meters)
606
+ otherVoices: snapshotsOf(others),
607
+ strongBeat: _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, beat, meters)
574
608
  };
575
- let result = _chunkYPB3A3EYcjs.evaluateSafety.call(void 0, safetyQuery, { suggestions: false });
609
+ let result = _chunk3R3QVKY5cjs.evaluateSafety.call(void 0, safetyQuery, { suggestions: false });
576
610
  if (result.safety !== 0 /* Safe */ && result.safety >= minSeverity) {
577
- result = _chunkYPB3A3EYcjs.evaluateSafety.call(void 0, safetyQuery);
611
+ result = _chunk3R3QVKY5cjs.evaluateSafety.call(void 0, safetyQuery, { partners: true });
578
612
  const conflict = {
579
613
  beat,
580
614
  trackName: track.name,
@@ -597,6 +631,11 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
597
631
  if (result.suggestions !== void 0) {
598
632
  conflict.suggestions = [...result.suggestions];
599
633
  }
634
+ if (result.partners !== void 0) {
635
+ conflict.partners = result.partners.map(
636
+ ({ index, reasons }) => conflictPartner(others[index], reasons)
637
+ );
638
+ }
600
639
  conflicts.push(conflict);
601
640
  }
602
641
  }
@@ -682,7 +721,7 @@ function sessionOf(tracks, opts, analysis, evidence) {
682
721
  tracks,
683
722
  analysis,
684
723
  update(edits) {
685
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, edits.length, "arrangement edits", opts.budget);
724
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, edits.length, "arrangement edits", opts.budget);
686
725
  const next = [...tracks];
687
726
  let edited = null;
688
727
  let reordered = false;
@@ -693,16 +732,16 @@ function sessionOf(tracks, opts, analysis, evidence) {
693
732
  }
694
733
  const target = next[edit.trackIndex];
695
734
  if (target === void 0) {
696
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
735
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
697
736
  `edits[${index}].trackIndex ${edit.trackIndex} is outside the session's ${tracks.length} tracks`
698
737
  );
699
738
  }
700
739
  if (!Array.isArray(edit.notes)) {
701
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
740
+ throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
702
741
  `edits[${index}].notes must be an array; received ${typeof edit.notes}`
703
742
  );
704
743
  }
705
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, edit.notes, `edits[${index}].notes`, {
744
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, edit.notes, `edits[${index}].notes`, {
706
745
  allowNonPositiveDuration: true,
707
746
  budget: opts.budget
708
747
  });
@@ -713,7 +752,7 @@ function sessionOf(tracks, opts, analysis, evidence) {
713
752
  if (edited === null && !reordered) {
714
753
  return sessionOf(next, opts, analysis, evidence);
715
754
  }
716
- const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } : _chunkQ6R3QAZDcjs.dirtySlotsFor.call(void 0, evidence, edited.startBeat, edited.endBeat);
755
+ const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } : _chunkIFZDWIKMcjs.dirtySlotsFor.call(void 0, evidence, edited.startBeat, edited.endBeat);
717
756
  const run = analyzeArrangementWith(next, opts, evidence, dirty);
718
757
  return sessionOf(next, opts, run.analysis, run.evidence);
719
758
  }
@@ -738,22 +777,22 @@ function clamp012(value) {
738
777
  return Math.min(1, Math.max(0, value));
739
778
  }
740
779
  function tensionCurve(tracks, opts = {}) {
741
- const meters = _chunkXKSO2V5Icjs.resolveMeters.call(void 0, opts, "arrangement meters");
780
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "arrangement meters");
742
781
  const budget = opts.budget;
743
782
  const noteOptions = { allowNonPositiveDuration: true, budget };
744
783
  if (opts.pickupBeats !== void 0) {
745
- _chunkXKSO2V5Icjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
784
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
746
785
  noteOptions.minStartBeat = -opts.pickupBeats;
747
786
  }
748
- _chunkXKSO2V5Icjs.assertArray.call(void 0, tracks, "arrangement tracks");
749
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
787
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, tracks, "arrangement tracks");
788
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
750
789
  const noteCount = assertTrackNotes(tracks, noteOptions);
751
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
790
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
752
791
  const profile = arrangementProfile(opts.profile);
753
792
  const step = _nullishCoalesce(opts.step, () => ( 1));
754
- _chunkXKSO2V5Icjs.assertRange.call(void 0, step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
793
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
755
794
  if (opts.minSeverity !== void 0) {
756
- _chunkXKSO2V5Icjs.assertInteger.call(void 0,
795
+ _chunkS5BKZZGMcjs.assertInteger.call(void 0,
757
796
  opts.minSeverity,
758
797
  "arrangement minSeverity",
759
798
  0 /* Safe */,
@@ -766,7 +805,7 @@ function tensionCurve(tracks, opts = {}) {
766
805
  const pooled = poolNotes(tracks, harmonyTracks);
767
806
  const all = poolNotes(tracks);
768
807
  const totalBeats = all.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
769
- const { origin: sampleStart, startBeat: musicStart } = _chunkQ6R3QAZDcjs.gridForNotes.call(void 0, all, step);
808
+ const { origin: sampleStart, startBeat: musicStart } = _chunkIFZDWIKMcjs.gridForNotes.call(void 0, all, step);
770
809
  const suppliedKeys = opts.timeline === void 0 ? void 0 : _nullishCoalesce(opts.keys, () => ( (opts.key !== void 0 ? [
771
810
  {
772
811
  // The one region a named key holds starts where the music does,
@@ -774,11 +813,11 @@ function tensionCurve(tracks, opts = {}) {
774
813
  // a key cannot be in force over beats nothing sounds in.
775
814
  startBeat: musicStart,
776
815
  endBeat: totalBeats,
777
- key: _chunkYPB3A3EYcjs.resolveKey.call(void 0, opts.key),
816
+ key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, opts.key),
778
817
  confidence: 1
779
818
  }
780
- ] : _chunkQ6R3QAZDcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }))));
781
- const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ? _chunkQ6R3QAZDcjs.chordTimelineFromNotes.call(void 0, pooled, {
819
+ ] : _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }))));
820
+ const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ? _chunkIFZDWIKMcjs.chordTimelineFromNotes.call(void 0, pooled, {
782
821
  key: opts.key,
783
822
  meters,
784
823
  pickupBeats: opts.pickupBeats,
@@ -787,16 +826,16 @@ function tensionCurve(tracks, opts = {}) {
787
826
  }) : {
788
827
  timeline: opts.timeline,
789
828
  keys: suppliedKeys,
790
- prevailingKey: _nullishCoalesce(_chunkQ6R3QAZDcjs.prevailingKeyOf.call(void 0, suppliedKeys), () => ( _chunkYPB3A3EYcjs.resolveKey.call(void 0, _chunkYPB3A3EYcjs.majorKey.call(void 0, 0))))
829
+ prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, suppliedKeys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))))
791
830
  };
792
831
  const regions = _nullishCoalesce(opts.keys, () => ( keys));
793
- const keyAt = _chunkQ6R3QAZDcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkQ6R3QAZDcjs.prevailingKeyOf.call(void 0, regions), () => ( prevailingKey)));
832
+ const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, regions), () => ( prevailingKey)));
794
833
  const prepared = prepareTracks(tracks);
795
834
  const points = [];
796
- const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step - _chunkXKSO2V5Icjs.BEAT_EPS));
797
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, sampleCount, "tension samples", budget);
835
+ const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step - _chunkS5BKZZGMcjs.BEAT_EPS));
836
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, sampleCount, "tension samples", budget);
798
837
  const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
799
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
838
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
800
839
  sampleCount * Math.min(128, voiceCount) * voiceCount,
801
840
  "tension sample-voice comparisons",
802
841
  budget
@@ -807,14 +846,14 @@ function tensionCurve(tracks, opts = {}) {
807
846
  beat,
808
847
  // The pitch classes alone: tension is scored from what sounds against
809
848
  // the tonic, and no reading below here asks how the key is written.
810
- tension: sampleTension(prepared, timeline, _chunkYPB3A3EYcjs.scaleOf.call(void 0, keyAt(beat)), meters, profile, beat)
849
+ tension: sampleTension(prepared, timeline, _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat)), meters, profile, beat)
811
850
  });
812
851
  }
813
852
  return points;
814
853
  }
815
854
  function tensionCurveFrom(tracks, analysis, opts = {}) {
816
- const asked = _chunkXKSO2V5Icjs.assertRecord.call(void 0, opts, "opts");
817
- const carried = carriedHarmony(_chunkXKSO2V5Icjs.assertRecord.call(void 0, analysis, "analysis"), asked);
855
+ const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
856
+ const carried = carriedHarmony(_chunkS5BKZZGMcjs.assertRecord.call(void 0, analysis, "analysis"), asked);
818
857
  return tensionCurve(tracks, { ...carried, ...asked });
819
858
  }
820
859
  function carriedHarmony(analysis, opts) {
@@ -848,9 +887,9 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
848
887
  return 0;
849
888
  }
850
889
  const chord = timeline.at(beat);
851
- const functionScore = chord ? FUNCTION_TENSION[_chunkQ6R3QAZDcjs.functionOf.call(void 0, chord, key)] : 0;
852
- const chordPcs = chord ? new Set(_chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, chord)) : null;
853
- const strongBeat = _chunkXKSO2V5Icjs.isStrongBeat.call(void 0, beat, meters);
890
+ const functionScore = chord ? FUNCTION_TENSION[_chunkIFZDWIKMcjs.functionOf.call(void 0, chord, key)] : 0;
891
+ const chordPcs = chord ? new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord)) : null;
892
+ const strongBeat = _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, beat, meters);
854
893
  let nonChord = 0;
855
894
  let dissonant = 0;
856
895
  const pitches = /* @__PURE__ */ new Map();
@@ -866,7 +905,7 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
866
905
  }
867
906
  }
868
907
  for (const [pitch, count] of pitches) {
869
- if (chordPcs && !chordPcs.has(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch))) {
908
+ if (chordPcs && !chordPcs.has(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch))) {
870
909
  nonChord += count;
871
910
  }
872
911
  const at = _nullishCoalesce(firstOf.get(pitch), () => ( 0));
@@ -877,7 +916,7 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
877
916
  others[at] = tail;
878
917
  others.pop();
879
918
  }
880
- const result = _chunkYPB3A3EYcjs.evaluateSafety.call(void 0,
919
+ const result = _chunk3R3QVKY5cjs.evaluateSafety.call(void 0,
881
920
  { profile, candidatePitch: pitch, chord, key, otherVoices: others, strongBeat },
882
921
  { suggestions: false }
883
922
  );
@@ -928,7 +967,7 @@ function accentContrast(novelty, slices, groupBars, phase) {
928
967
  continue;
929
968
  }
930
969
  const value = _nullishCoalesce(novelty[i], () => ( 0));
931
- if (_chunkQ6R3QAZDcjs.floorMod.call(void 0, slice.index - phase, groupBars) === 0) {
970
+ if (_chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - phase, groupBars) === 0) {
932
971
  headSum += value;
933
972
  headCount += 1;
934
973
  } else {
@@ -946,7 +985,7 @@ function cadenceFitOf(cadenceBars, slices, groupBars, phase) {
946
985
  let ends = 0;
947
986
  let fit = 0;
948
987
  for (const slice of slices) {
949
- if (slice.index < 0 || _chunkQ6R3QAZDcjs.floorMod.call(void 0, slice.index - phase, groupBars) !== groupBars - 1) {
988
+ if (slice.index < 0 || _chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - phase, groupBars) !== groupBars - 1) {
950
989
  continue;
951
990
  }
952
991
  ends += 1;
@@ -956,7 +995,7 @@ function cadenceFitOf(cadenceBars, slices, groupBars, phase) {
956
995
  fit += ELIDED_FIT;
957
996
  }
958
997
  }
959
- return ends === 0 ? 0 : _chunkQ6R3QAZDcjs.clamp01.call(void 0, fit / ends);
998
+ return ends === 0 ? 0 : _chunkIFZDWIKMcjs.clamp01.call(void 0, fit / ends);
960
999
  }
961
1000
  function undecidedReading(slices) {
962
1001
  const fullBars = slices.filter((slice) => slice.index >= 0);
@@ -970,31 +1009,31 @@ function undecidedReading(slices) {
970
1009
  };
971
1010
  }
972
1011
  function hypermeter(notes, meter, opts = {}) {
973
- const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
974
- const meters = _chunkXKSO2V5Icjs.resolveMeters.call(void 0, { meters: meter }, "meter");
975
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, "hypermeter notes", {
1012
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
1013
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, { meters: meter }, "meter");
1014
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "hypermeter notes", {
976
1015
  allowNonPositiveDuration: true,
977
1016
  budget: asked.budget
978
1017
  });
979
1018
  const sounding = notes.filter((note) => note.durationBeat > 0);
980
- const firstOnset = _chunkQ6R3QAZDcjs.firstSoundingBeat.call(void 0, sounding);
1019
+ const firstOnset = _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding);
981
1020
  const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
982
1021
  const spanEnd = _nullishCoalesce(asked.totalBeats, () => ( lastEnd));
983
- _chunkXKSO2V5Icjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "hypermeter totalBeats");
984
- const spanStart = _chunkQ6R3QAZDcjs.barGridStart.call(void 0, firstOnset, meters);
985
- const slices = _chunkQ6R3QAZDcjs.sliceBars.call(void 0, sounding, meters, spanStart, spanEnd, asked.budget);
1022
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "hypermeter totalBeats");
1023
+ const spanStart = _chunkIFZDWIKMcjs.barGridStart.call(void 0, firstOnset, meters);
1024
+ const slices = _chunkIFZDWIKMcjs.sliceBars.call(void 0, sounding, meters, spanStart, spanEnd, asked.budget);
986
1025
  const fullBars = slices.reduce((count, slice) => slice.index >= 0 ? count + 1 : count, 0);
987
1026
  const candidates = GROUP_CANDIDATES.filter((groupBars) => fullBars >= groupBars * MIN_GROUPS);
988
1027
  if (candidates.length === 0) {
989
1028
  return undecidedReading(slices);
990
1029
  }
991
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
1030
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
992
1031
  candidates.reduce((sum, groupBars) => sum + groupBars * slices.length, 0),
993
1032
  "hypermeter readings",
994
1033
  asked.budget
995
1034
  );
996
- const novelty = slices.map((_, index) => _chunkQ6R3QAZDcjs.harmonicNovelty.call(void 0, slices, index));
997
- const cadenceBars = new Set((_nullishCoalesce(asked.cadenceBeats, () => ( []))).map((beat) => _chunkXKSO2V5Icjs.barIndexAt.call(void 0, beat, meters)));
1035
+ const novelty = slices.map((_, index) => _chunkIFZDWIKMcjs.harmonicNovelty.call(void 0, slices, index));
1036
+ const cadenceBars = new Set((_nullishCoalesce(asked.cadenceBeats, () => ( []))).map((beat) => _chunkS5BKZZGMcjs.barIndexAt.call(void 0, beat, meters)));
998
1037
  const hasCadences = cadenceBars.size > 0;
999
1038
  const readings = [];
1000
1039
  for (const groupBars of candidates) {
@@ -1005,17 +1044,17 @@ function hypermeter(notes, meter, opts = {}) {
1005
1044
  }));
1006
1045
  const bestFit = phases.reduce((best2, reading) => Math.max(best2, reading.cadenceFit), 0);
1007
1046
  for (const { phase, contrast, cadenceFit } of phases) {
1008
- if (hasCadences && cadenceFit < bestFit - _chunkXKSO2V5Icjs.BEAT_EPS) {
1047
+ if (hasCadences && cadenceFit < bestFit - _chunkS5BKZZGMcjs.BEAT_EPS) {
1009
1048
  continue;
1010
1049
  }
1011
- const evidence = _chunkQ6R3QAZDcjs.clamp01.call(void 0,
1050
+ const evidence = _chunkIFZDWIKMcjs.clamp01.call(void 0,
1012
1051
  hasCadences ? (
1013
1052
  // Floored at zero rather than summed raw: group heads that restate
1014
1053
  // the harmony their group opened with — the commonest way a phrase
1015
1054
  // begins — change less than the bars between them, and a negative
1016
1055
  // contrast subtracted from the cadence term would count that
1017
1056
  // restatement as evidence against the very phase it confirms.
1018
- CONTRAST_SHARE * _chunkQ6R3QAZDcjs.clamp01.call(void 0, contrast) + CADENCE_SHARE * cadenceFit
1057
+ CONTRAST_SHARE * _chunkIFZDWIKMcjs.clamp01.call(void 0, contrast) + CADENCE_SHARE * cadenceFit
1019
1058
  ) : contrast
1020
1059
  );
1021
1060
  readings.push({
@@ -1036,8 +1075,8 @@ function hypermeter(notes, meter, opts = {}) {
1036
1075
  return undecidedReading(slices);
1037
1076
  }
1038
1077
  const runnerUp = readings.find((reading) => reading.groupBars !== best.groupBars);
1039
- const margin = runnerUp === void 0 || best.score <= _chunkXKSO2V5Icjs.BEAT_EPS ? 1 : _chunkQ6R3QAZDcjs.clamp01.call(void 0, (best.score - runnerUp.score) / best.score);
1040
- const downbeats = slices.filter((slice) => slice.index >= 0 && _chunkQ6R3QAZDcjs.floorMod.call(void 0, slice.index - best.phase, best.groupBars) === 0).map((slice) => slice.startBeat);
1078
+ const margin = runnerUp === void 0 || best.score <= _chunkS5BKZZGMcjs.BEAT_EPS ? 1 : _chunkIFZDWIKMcjs.clamp01.call(void 0, (best.score - runnerUp.score) / best.score);
1079
+ const downbeats = slices.filter((slice) => slice.index >= 0 && _chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - best.phase, best.groupBars) === 0).map((slice) => slice.startBeat);
1041
1080
  const cadencePart = hasCadences ? `${Math.round(best.cadenceFit * 100)}% of group ends closed by a cadence` : "no cadences given";
1042
1081
  return {
1043
1082
  groupBars: best.groupBars,
@@ -1047,554 +1086,11 @@ function hypermeter(notes, meter, opts = {}) {
1047
1086
  // runner-up is nearly always a multiple or a divisor of the winner — four
1048
1087
  // bars nest inside eight — and a reading of the same accents at another
1049
1088
  // level is weaker counter-evidence than an unrelated rival would be.
1050
- confidence: _chunkQ6R3QAZDcjs.clamp01.call(void 0, best.evidence * (MARGIN_FLOOR + (1 - MARGIN_FLOOR) * margin)),
1089
+ confidence: _chunkIFZDWIKMcjs.clamp01.call(void 0, best.evidence * (MARGIN_FLOOR + (1 - MARGIN_FLOOR) * margin)),
1051
1090
  rationale: `${best.groupBars}-bar groups from bar ${best.phase}: harmonic change ${Math.round(best.contrast * 100)}% stronger on group heads, ${cadencePart}`
1052
1091
  };
1053
1092
  }
1054
1093
 
1055
- // src/analyze/melody/internal.ts
1056
- var RHYTHM_GRID = 64;
1057
- function phraseNotes(phrase) {
1058
- if (Array.isArray(phrase)) {
1059
- return phrase;
1060
- }
1061
- return _chunkXKSO2V5Icjs.assertRecord.call(void 0, phrase, "melodic phrase").notes;
1062
- }
1063
- function orderedNotes(notes, name, budget) {
1064
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, name, { allowNonPositiveDuration: true, budget });
1065
- const sounding = notes.filter((note) => note.durationBeat > 0).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
1066
- const line = [];
1067
- for (const note of sounding) {
1068
- const previous = line[line.length - 1];
1069
- if (previous !== void 0 && Math.abs(note.startBeat - previous.startBeat) <= _chunkXKSO2V5Icjs.BEAT_EPS) {
1070
- line[line.length - 1] = note;
1071
- continue;
1072
- }
1073
- line.push(note);
1074
- }
1075
- return line;
1076
- }
1077
- function intervalsOf(notes) {
1078
- const out = [];
1079
- for (let i = 1; i < notes.length; i += 1) {
1080
- out.push((_nullishCoalesce(_optionalChain([notes, 'access', _43 => _43[i], 'optionalAccess', _44 => _44.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _45 => _45[i - 1], 'optionalAccess', _46 => _46.pitch]), () => ( 0))));
1081
- }
1082
- return out;
1083
- }
1084
- function onsetGaps(notes) {
1085
- const out = [];
1086
- for (let i = 1; i < notes.length; i += 1) {
1087
- out.push((_nullishCoalesce(_optionalChain([notes, 'access', _47 => _47[i], 'optionalAccess', _48 => _48.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _49 => _49[i - 1], 'optionalAccess', _50 => _50.startBeat]), () => ( 0))));
1088
- }
1089
- return out;
1090
- }
1091
- function retrogradeGaps(notes) {
1092
- const out = [];
1093
- for (let i = notes.length - 1; i >= 1; i -= 1) {
1094
- const later = notes[i];
1095
- const earlier = notes[i - 1];
1096
- out.push(
1097
- (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _51 => _51.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _52 => _52.durationBeat]), () => ( 0))) - ((_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _53 => _53.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _54 => _54.durationBeat]), () => ( 0))))
1098
- );
1099
- }
1100
- return out;
1101
- }
1102
- function rhythmProfile(gaps) {
1103
- const unit = _nullishCoalesce(gaps[0], () => ( 0));
1104
- if (!(unit > _chunkXKSO2V5Icjs.BEAT_EPS)) {
1105
- return gaps.length === 0 ? [] : null;
1106
- }
1107
- return gaps.map((gap) => Math.round(gap / unit * RHYTHM_GRID) / RHYTHM_GRID);
1108
- }
1109
- function sameNumbers(a, b) {
1110
- if (a.length !== b.length) {
1111
- return false;
1112
- }
1113
- for (let i = 0; i < a.length; i += 1) {
1114
- if (Math.abs((_nullishCoalesce(a[i], () => ( 0))) - (_nullishCoalesce(b[i], () => ( 0)))) > _chunkXKSO2V5Icjs.BEAT_EPS) {
1115
- return false;
1116
- }
1117
- }
1118
- return true;
1119
- }
1120
- function roundTo(value, digits) {
1121
- const scale = 10 ** digits;
1122
- return Math.round(value * scale) / scale;
1123
- }
1124
- function endBeatOf(notes) {
1125
- let end = 0;
1126
- for (const note of notes) {
1127
- end = Math.max(end, note.startBeat + note.durationBeat);
1128
- }
1129
- return end;
1130
- }
1131
- function onsetSpan(notes) {
1132
- if (notes.length < 2) {
1133
- return 0;
1134
- }
1135
- return (_nullishCoalesce(_optionalChain([notes, 'access', _55 => _55[notes.length - 1], 'optionalAccess', _56 => _56.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _57 => _57[0], 'optionalAccess', _58 => _58.startBeat]), () => ( 0)));
1136
- }
1137
-
1138
- // src/analyze/melody/contour.ts
1139
- var DIRECTIONAL_SHARE = 0.75;
1140
- function extremes(notes) {
1141
- if (notes.length === 0) {
1142
- return { peak: -1, trough: -1 };
1143
- }
1144
- let peak = 0;
1145
- let trough = 0;
1146
- for (let i = 1; i < notes.length; i += 1) {
1147
- if ((_nullishCoalesce(_optionalChain([notes, 'access', _59 => _59[i], 'optionalAccess', _60 => _60.pitch]), () => ( 0))) > (_nullishCoalesce(_optionalChain([notes, 'access', _61 => _61[peak], 'optionalAccess', _62 => _62.pitch]), () => ( 0)))) {
1148
- peak = i;
1149
- }
1150
- if ((_nullishCoalesce(_optionalChain([notes, 'access', _63 => _63[i], 'optionalAccess', _64 => _64.pitch]), () => ( 0))) < (_nullishCoalesce(_optionalChain([notes, 'access', _65 => _65[trough], 'optionalAccess', _66 => _66.pitch]), () => ( 0)))) {
1151
- trough = i;
1152
- }
1153
- }
1154
- return { peak, trough };
1155
- }
1156
- function melodicContour(notes) {
1157
- const line = orderedNotes(phraseNotes(notes), "contour notes");
1158
- const intervals = intervalsOf(line);
1159
- const directions = intervals.map(
1160
- (step) => step > 0 ? "up" : step < 0 ? "down" : "same"
1161
- );
1162
- const { peak, trough } = extremes(line);
1163
- const range = (_nullishCoalesce(_optionalChain([line, 'access', _67 => _67[peak], 'optionalAccess', _68 => _68.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([line, 'access', _69 => _69[trough], 'optionalAccess', _70 => _70.pitch]), () => ( 0)));
1164
- const motion = intervals.reduce((sum, step) => sum + Math.abs(step), 0);
1165
- if (motion === 0) {
1166
- return {
1167
- directions,
1168
- shape: "static",
1169
- peakIndex: peak,
1170
- troughIndex: trough,
1171
- range: 0,
1172
- rationale: line.length < 2 ? "Too short to have a direction, so the line is static" : "Every note at the same pitch, so the line is static"
1173
- };
1174
- }
1175
- const first = _nullishCoalesce(_optionalChain([line, 'access', _71 => _71[0], 'optionalAccess', _72 => _72.pitch]), () => ( 0));
1176
- const last = _nullishCoalesce(_optionalChain([line, 'access', _73 => _73[line.length - 1], 'optionalAccess', _74 => _74.pitch]), () => ( 0));
1177
- const net = last - first;
1178
- const directness = Math.abs(net) / motion;
1179
- const share = Math.round(directness * 100);
1180
- if (directness >= DIRECTIONAL_SHARE) {
1181
- const shape = net > 0 ? "ascending" : "descending";
1182
- return {
1183
- directions,
1184
- shape,
1185
- peakIndex: peak,
1186
- troughIndex: trough,
1187
- range,
1188
- rationale: `${share}% of the motion runs ${net > 0 ? "up" : "down"}, so the line is ${shape}`
1189
- };
1190
- }
1191
- const peakPitch = _nullishCoalesce(_optionalChain([line, 'access', _75 => _75[peak], 'optionalAccess', _76 => _76.pitch]), () => ( 0));
1192
- const archMotion = peakPitch - first + (peakPitch - last);
1193
- if (peak > 0 && peak < line.length - 1 && archMotion >= DIRECTIONAL_SHARE * motion) {
1194
- return {
1195
- directions,
1196
- shape: "arch",
1197
- peakIndex: peak,
1198
- troughIndex: trough,
1199
- range,
1200
- rationale: `Rises to its peak at note ${peak + 1} of ${line.length} and falls back, so the line is an arch`
1201
- };
1202
- }
1203
- return {
1204
- directions,
1205
- shape: "wave",
1206
- peakIndex: peak,
1207
- troughIndex: trough,
1208
- range,
1209
- rationale: `Changes direction without settling either way (${share}% of the motion nets out), so the line is a wave`
1210
- };
1211
- }
1212
-
1213
- // src/analyze/melody/motifs.ts
1214
- var MIN_CELL_NOTES = 3;
1215
- var MAX_CELL_NOTES = 8;
1216
- var MIN_OCCURRENCES = 2;
1217
- function windowSignature(notes) {
1218
- const profile = rhythmProfile(onsetGaps(notes));
1219
- if (profile === null) {
1220
- return null;
1221
- }
1222
- return `${notes.length}|${intervalsOf(notes).join(",")}|${profile.join(",")}`;
1223
- }
1224
- function nonOverlapping(starts, length) {
1225
- const picked = [];
1226
- let lastEnd = Number.NEGATIVE_INFINITY;
1227
- for (const start of starts) {
1228
- if (start > lastEnd) {
1229
- picked.push(start);
1230
- lastEnd = start + length - 1;
1231
- }
1232
- }
1233
- return picked;
1234
- }
1235
- function subsumes(outer, inner) {
1236
- let at = 0;
1237
- for (const start of inner.starts) {
1238
- while (at + 1 < outer.starts.length && (_nullishCoalesce(outer.starts[at + 1], () => ( 0))) <= start) {
1239
- at += 1;
1240
- }
1241
- const from = outer.starts[at];
1242
- if (from === void 0 || start < from || start + inner.length > from + outer.length) {
1243
- return false;
1244
- }
1245
- }
1246
- return true;
1247
- }
1248
- function listBeats(occurrences) {
1249
- const shown = occurrences.slice(0, 5).map((occurrence) => roundTo(occurrence.startBeat, 3));
1250
- return occurrences.length > shown.length ? `${shown.join(", ")}, and ${occurrences.length - shown.length} more` : shown.join(", ");
1251
- }
1252
- function motifFromGroup(group, notes) {
1253
- const first = _nullishCoalesce(group.starts[0], () => ( 0));
1254
- const prime = notes.slice(first, first + group.length).map((note) => ({ ...note }));
1255
- const primePitch = _nullishCoalesce(_optionalChain([prime, 'access', _77 => _77[0], 'optionalAccess', _78 => _78.pitch]), () => ( 0));
1256
- const primeSpan = onsetSpan(prime);
1257
- const occurrences = group.starts.map((start) => {
1258
- const statement = notes.slice(start, start + group.length);
1259
- const span = onsetSpan(statement);
1260
- return {
1261
- noteIndex: start,
1262
- startBeat: _nullishCoalesce(_optionalChain([statement, 'access', _79 => _79[0], 'optionalAccess', _80 => _80.startBeat]), () => ( 0)),
1263
- endBeat: endBeatOf(statement),
1264
- transpose: (_nullishCoalesce(_optionalChain([statement, 'access', _81 => _81[0], 'optionalAccess', _82 => _82.pitch]), () => ( 0))) - primePitch,
1265
- timeRatio: primeSpan > _chunkXKSO2V5Icjs.BEAT_EPS ? span / primeSpan : 1
1266
- };
1267
- });
1268
- const shape = melodicContour(prime).shape;
1269
- const transposed = occurrences.some((occurrence) => occurrence.transpose !== 0);
1270
- const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > _chunkXKSO2V5Icjs.BEAT_EPS);
1271
- const detail = [
1272
- transposed ? "at more than one pitch level" : "at the same pitch level",
1273
- stretched ? "and in more than one set of note values" : ""
1274
- ].filter((part) => part !== "").join(" ");
1275
- return {
1276
- notes: prime,
1277
- intervals: intervalsOf(prime),
1278
- rhythm: _nullishCoalesce(rhythmProfile(onsetGaps(prime)), () => ( [])),
1279
- occurrences,
1280
- rationale: `${group.length}-note ${shape} cell stated ${occurrences.length} ${occurrences.length === 1 ? "time" : "times"} (beats ${listBeats(occurrences)}), ${detail}`
1281
- };
1282
- }
1283
- function extractMotifs(notes, opts = {}) {
1284
- const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
1285
- const minNotes = _chunkXKSO2V5Icjs.assertInteger.call(void 0, _nullishCoalesce(asked.minNotes, () => ( MIN_CELL_NOTES)), "motif minNotes", 2, 64);
1286
- const maxNotes = _chunkXKSO2V5Icjs.assertInteger.call(void 0, _nullishCoalesce(asked.maxNotes, () => ( MAX_CELL_NOTES)), "motif maxNotes", 2, 64);
1287
- if (maxNotes < minNotes) {
1288
- throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
1289
- `motif maxNotes must be at least minNotes ${minNotes}; received ${maxNotes}`
1290
- );
1291
- }
1292
- const minOccurrences = _chunkXKSO2V5Icjs.assertInteger.call(void 0,
1293
- _nullishCoalesce(asked.minOccurrences, () => ( MIN_OCCURRENCES)),
1294
- "motif minOccurrences",
1295
- 2
1296
- );
1297
- const sounding = orderedNotes(notes, "melody notes", asked.budget);
1298
- if (sounding.length < minNotes) {
1299
- return [];
1300
- }
1301
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
1302
- sounding.length * (maxNotes - minNotes + 1),
1303
- "motif windows",
1304
- asked.budget
1305
- );
1306
- const groups = /* @__PURE__ */ new Map();
1307
- for (let length = minNotes; length <= maxNotes; length += 1) {
1308
- for (let start = 0; start + length <= sounding.length; start += 1) {
1309
- const signature = windowSignature(sounding.slice(start, start + length));
1310
- if (signature === null) {
1311
- continue;
1312
- }
1313
- const group = groups.get(signature);
1314
- if (group === void 0) {
1315
- groups.set(signature, { signature, length, starts: [start] });
1316
- } else {
1317
- group.starts.push(start);
1318
- }
1319
- }
1320
- }
1321
- const candidates = [...groups.values()].map((group) => ({ ...group, starts: nonOverlapping(group.starts, group.length) })).filter((group) => group.starts.length >= minOccurrences).sort(
1322
- (a, b) => b.length - a.length || b.starts.length - a.starts.length || (_nullishCoalesce(a.starts[0], () => ( 0))) - (_nullishCoalesce(b.starts[0], () => ( 0))) || (a.signature < b.signature ? -1 : a.signature > b.signature ? 1 : 0)
1323
- );
1324
- const keptByLength = /* @__PURE__ */ new Map();
1325
- const kept = [];
1326
- let comparisons = 0;
1327
- for (const candidate of candidates) {
1328
- const first = _nullishCoalesce(candidate.starts[0], () => ( 0));
1329
- let covered = false;
1330
- for (let length = candidate.length + 1; length <= maxNotes && !covered; length += 1) {
1331
- const byStart = keptByLength.get(length);
1332
- if (byStart === void 0) {
1333
- continue;
1334
- }
1335
- for (let from = Math.max(0, first - (length - candidate.length)); from <= first; from += 1) {
1336
- comparisons += 1;
1337
- const outer = byStart.get(from);
1338
- if (outer === void 0 || outer.starts.length < candidate.starts.length) {
1339
- continue;
1340
- }
1341
- comparisons += outer.starts.length + candidate.starts.length;
1342
- if (subsumes(outer, candidate)) {
1343
- covered = true;
1344
- break;
1345
- }
1346
- }
1347
- }
1348
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, comparisons, "motif subsumption comparisons", asked.budget);
1349
- if (!covered) {
1350
- kept.push(candidate);
1351
- let byStart = keptByLength.get(candidate.length);
1352
- if (byStart === void 0) {
1353
- byStart = /* @__PURE__ */ new Map();
1354
- keptByLength.set(candidate.length, byStart);
1355
- }
1356
- for (const start of candidate.starts) {
1357
- byStart.set(start, candidate);
1358
- }
1359
- }
1360
- }
1361
- return kept.map((group) => motifFromGroup(group, sounding));
1362
- }
1363
- function motifFromNotes(notes) {
1364
- const sounding = orderedNotes(notes, "motif notes").map((note) => ({ ...note }));
1365
- return motifFromGroup(
1366
- { signature: _nullishCoalesce(windowSignature(sounding), () => ( "")), length: sounding.length, starts: [0] },
1367
- sounding
1368
- );
1369
- }
1370
-
1371
- // src/analyze/melody/relation.ts
1372
- function spellingOf(key) {
1373
- if (key === void 0) {
1374
- return "sharp";
1375
- }
1376
- return _chunkYPB3A3EYcjs.keySignatureFifths.call(void 0, key.tonic, key.scale) < 0 ? "flat" : "sharp";
1377
- }
1378
- function intervalBetween(from, to, key) {
1379
- const spelling = spellingOf(key);
1380
- return _chunkXKSO2V5Icjs.spelledInterval.call(void 0, _chunkXKSO2V5Icjs.midiToNote.call(void 0, from, spelling), _chunkXKSO2V5Icjs.midiToNote.call(void 0, to, spelling));
1381
- }
1382
- function intervalName(interval) {
1383
- return `${interval.descending && interval.semitones !== 0 ? "-" : ""}${interval.quality}${interval.number}`;
1384
- }
1385
- function ladderIndices(notes, key) {
1386
- const root = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc);
1387
- const offsets = _chunkYPB3A3EYcjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pc - root));
1388
- if (offsets.length === 0) {
1389
- return null;
1390
- }
1391
- const out = [];
1392
- for (const note of notes) {
1393
- const semitones = note.pitch - root;
1394
- const octave = Math.floor(semitones / 12);
1395
- const degree = offsets.indexOf(semitones - octave * 12);
1396
- if (degree < 0) {
1397
- return null;
1398
- }
1399
- out.push(octave * offsets.length + degree);
1400
- }
1401
- return out;
1402
- }
1403
- function ladderSteps(indices) {
1404
- const out = [];
1405
- for (let i = 1; i < indices.length; i += 1) {
1406
- out.push((_nullishCoalesce(indices[i], () => ( 0))) - (_nullishCoalesce(indices[i - 1], () => ( 0))));
1407
- }
1408
- return out;
1409
- }
1410
- function pitchShapes(a, b) {
1411
- const shapes = [];
1412
- if (sameNumbers(a, b)) {
1413
- shapes.push("exact");
1414
- }
1415
- if (sameNumbers(
1416
- b,
1417
- a.map((step) => -step)
1418
- )) {
1419
- shapes.push("inversion");
1420
- }
1421
- const reversed = [...a].reverse();
1422
- if (sameNumbers(
1423
- b,
1424
- reversed.map((step) => -step)
1425
- )) {
1426
- shapes.push("retrograde");
1427
- }
1428
- if (sameNumbers(b, reversed)) {
1429
- shapes.push("retrogradeInversion");
1430
- }
1431
- return shapes;
1432
- }
1433
- function totalGap(gaps) {
1434
- return gaps.reduce((sum, gap) => sum + gap, 0);
1435
- }
1436
- function stretchRatio(modelGaps, answerGaps) {
1437
- const span = totalGap(modelGaps);
1438
- return span > _chunkXKSO2V5Icjs.BEAT_EPS ? totalGap(answerGaps) / span : 1;
1439
- }
1440
- function stretchPhrase(ratio) {
1441
- return `note values ${roundTo(ratio, 3)}x the model's`;
1442
- }
1443
- function relateMotifs(a, b, keyLike) {
1444
- const key = keyLike === void 0 ? void 0 : _chunkYPB3A3EYcjs.resolveKey.call(void 0, keyLike);
1445
- const model = _chunkXKSO2V5Icjs.assertArray.call(void 0, _chunkXKSO2V5Icjs.assertRecord.call(void 0, a, "a").notes, "a.notes");
1446
- const answer = _chunkXKSO2V5Icjs.assertArray.call(void 0, _chunkXKSO2V5Icjs.assertRecord.call(void 0, b, "b").notes, "b.notes");
1447
- if (model.length === 0 || model.length !== answer.length) {
1448
- return null;
1449
- }
1450
- const modelGaps = onsetGaps(model);
1451
- const answerGaps = onsetGaps(answer);
1452
- const modelProfile = rhythmProfile(modelGaps);
1453
- const answerProfile = rhythmProfile(answerGaps);
1454
- const retroGaps = retrogradeGaps(model);
1455
- const retroProfile = rhythmProfile(retroGaps);
1456
- if (modelProfile === null || answerProfile === null) {
1457
- return null;
1458
- }
1459
- const forward = sameNumbers(modelProfile, answerProfile);
1460
- const backward = retroProfile !== null && sameNumbers(retroProfile, answerProfile);
1461
- const semitones = (_nullishCoalesce(_optionalChain([answer, 'access', _83 => _83[0], 'optionalAccess', _84 => _84.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([model, 'access', _85 => _85[0], 'optionalAccess', _86 => _86.pitch]), () => ( 0)));
1462
- const interval = intervalBetween(_nullishCoalesce(_optionalChain([model, 'access', _87 => _87[0], 'optionalAccess', _88 => _88.pitch]), () => ( 0)), _nullishCoalesce(_optionalChain([answer, 'access', _89 => _89[0], 'optionalAccess', _90 => _90.pitch]), () => ( 0)), key);
1463
- const gap = (_nullishCoalesce(_optionalChain([answer, 'access', _91 => _91[0], 'optionalAccess', _92 => _92.startBeat]), () => ( 0))) - endBeatOf(model);
1464
- const sequence = gap >= -_chunkQ6R3QAZDcjs.HUMANIZE_ADJACENCY && gap <= _chunkQ6R3QAZDcjs.HUMANIZE_ADJACENCY;
1465
- const timeRatio = stretchRatio(modelGaps, answerGaps);
1466
- const stretched = Math.abs(timeRatio - 1) > _chunkXKSO2V5Icjs.BEAT_EPS;
1467
- const shapes = pitchShapes(intervalsOf(model), intervalsOf(answer));
1468
- const base = { sequence, semitones, interval, timeRatio };
1469
- const backwardBase = { ...base, timeRatio: stretchRatio(retroGaps, answerGaps) };
1470
- if (forward && shapes.includes("exact")) {
1471
- if (semitones === 0) {
1472
- if (!stretched) {
1473
- return {
1474
- ...base,
1475
- kind: "repetition",
1476
- rationale: "Exact repetition: the same pitches in the same rhythm"
1477
- };
1478
- }
1479
- const augmented = timeRatio > 1;
1480
- return {
1481
- ...base,
1482
- kind: augmented ? "augmentation" : "diminution",
1483
- rationale: `${augmented ? "Augmentation" : "Diminution"}: the same pitches in ${stretchPhrase(timeRatio)}`
1484
- };
1485
- }
1486
- const name = intervalName(interval);
1487
- return {
1488
- ...base,
1489
- kind: "transposition",
1490
- rationale: sequence ? `Real sequence: the cell restated ${name} away with every interval kept literally` + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "") : `Transposition by ${name}: every interval kept literally` + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
1491
- };
1492
- }
1493
- if (forward && key !== void 0) {
1494
- const modelLadder = ladderIndices(model, key.scale);
1495
- const answerLadder = ladderIndices(answer, key.scale);
1496
- if (modelLadder !== null && answerLadder !== null) {
1497
- const degrees = (_nullishCoalesce(answerLadder[0], () => ( 0))) - (_nullishCoalesce(modelLadder[0], () => ( 0)));
1498
- if (degrees !== 0 && sameNumbers(ladderSteps(modelLadder), ladderSteps(answerLadder))) {
1499
- const direction = degrees > 0 ? "higher" : "lower";
1500
- const count = Math.abs(degrees);
1501
- return {
1502
- ...base,
1503
- kind: "tonalTransposition",
1504
- degrees,
1505
- rationale: `${sequence ? "Tonal sequence" : "Tonal transposition"}: the cell restated ${count} scale ${count === 1 ? "degree" : "degrees"} ${direction} inside the key, so the key resizes its intervals \u2014 a real one would have kept them literal`
1506
- };
1507
- }
1508
- }
1509
- }
1510
- if (forward && shapes.includes("inversion")) {
1511
- return {
1512
- ...base,
1513
- kind: "inversion",
1514
- rationale: "Inversion: every interval turned upside down about the first note" + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
1515
- };
1516
- }
1517
- if (backward && shapes.includes("retrograde")) {
1518
- return {
1519
- ...backwardBase,
1520
- kind: "retrograde",
1521
- rationale: "Retrograde: the cell read back to front, rhythm included"
1522
- };
1523
- }
1524
- if (backward && shapes.includes("retrogradeInversion")) {
1525
- return {
1526
- ...backwardBase,
1527
- kind: "retrogradeInversion",
1528
- rationale: "Retrograde inversion: the cell read back to front and turned upside down"
1529
- };
1530
- }
1531
- return null;
1532
- }
1533
-
1534
- // src/analyze/melody/similarity.ts
1535
- var INTERVAL_TOLERANCE = 6;
1536
- var PITCH_WEIGHT = 0.75;
1537
- var RHYTHM_WEIGHT = 0.25;
1538
- function gradedEditDistance(a, b, cost) {
1539
- let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
1540
- for (let i = 1; i <= a.length; i += 1) {
1541
- const row = new Array(b.length + 1);
1542
- row[0] = i;
1543
- for (let j = 1; j <= b.length; j += 1) {
1544
- const substitution = (_nullishCoalesce(previous[j - 1], () => ( 0))) + cost(_nullishCoalesce(a[i - 1], () => ( 0)), _nullishCoalesce(b[j - 1], () => ( 0)));
1545
- const deletion = (_nullishCoalesce(previous[j], () => ( 0))) + 1;
1546
- const insertion = (_nullishCoalesce(row[j - 1], () => ( 0))) + 1;
1547
- row[j] = Math.min(substitution, deletion, insertion);
1548
- }
1549
- previous = row;
1550
- }
1551
- return _nullishCoalesce(previous[b.length], () => ( 0));
1552
- }
1553
- function distanceToSimilarity(distance, a, b) {
1554
- const longest = Math.max(a, b);
1555
- if (longest === 0) {
1556
- return 1;
1557
- }
1558
- return Math.min(1, Math.max(0, 1 - distance / longest));
1559
- }
1560
- function intervalCost(x, y) {
1561
- return Math.min(1, Math.abs(x - y) / INTERVAL_TOLERANCE);
1562
- }
1563
- function gapCost(x, y) {
1564
- if (!(x > _chunkXKSO2V5Icjs.BEAT_EPS) || !(y > _chunkXKSO2V5Icjs.BEAT_EPS)) {
1565
- return Math.abs(x - y) > _chunkXKSO2V5Icjs.BEAT_EPS ? 1 : 0;
1566
- }
1567
- return Math.min(1, Math.abs(Math.log2(x / y)));
1568
- }
1569
- function compareMelodies(a, b) {
1570
- const left = orderedNotes(phraseNotes(a), "melody a");
1571
- const right = orderedNotes(phraseNotes(b), "melody b");
1572
- const leftIntervals = intervalsOf(left);
1573
- const rightIntervals = intervalsOf(right);
1574
- const pitchSimilarity = distanceToSimilarity(
1575
- gradedEditDistance(leftIntervals, rightIntervals, intervalCost),
1576
- leftIntervals.length,
1577
- rightIntervals.length
1578
- );
1579
- const leftGaps = _nullishCoalesce(rhythmProfile(onsetGaps(left)), () => ( onsetGaps(left)));
1580
- const rightGaps = _nullishCoalesce(rhythmProfile(onsetGaps(right)), () => ( onsetGaps(right)));
1581
- const rhythmSimilarity = distanceToSimilarity(
1582
- gradedEditDistance(leftGaps, rightGaps, gapCost),
1583
- leftGaps.length,
1584
- rightGaps.length
1585
- );
1586
- const similarity = PITCH_WEIGHT * pitchSimilarity + RHYTHM_WEIGHT * rhythmSimilarity;
1587
- return {
1588
- similarity,
1589
- pitchSimilarity,
1590
- rhythmSimilarity,
1591
- rationale: `Interval shapes ${Math.round(pitchSimilarity * 100)}% alike, rhythms ${Math.round(rhythmSimilarity * 100)}% alike, weighted ${PITCH_WEIGHT}/${RHYTHM_WEIGHT} into ${roundTo(similarity, 3)}`
1592
- };
1593
- }
1594
- function melodicSimilarity(a, b) {
1595
- return compareMelodies(a, b).similarity;
1596
- }
1597
-
1598
1094
  // src/analyze/form/phrase.ts
1599
1095
  var CADENCE_BOUNDARY_STRENGTH = {
1600
1096
  authentic: 0.9,
@@ -1632,7 +1128,7 @@ var SIGNAL_ORDER = [
1632
1128
  "hypermeter"
1633
1129
  ];
1634
1130
  function addSignal(boundaries, beat, signal, strength, cadence = null) {
1635
- const key = Math.round(beat / _chunkXKSO2V5Icjs.BEAT_EPS);
1131
+ const key = Math.round(beat / _chunkS5BKZZGMcjs.BEAT_EPS);
1636
1132
  const existing = boundaries.get(key);
1637
1133
  const boundary = _nullishCoalesce(existing, () => ( { beat, strengths: /* @__PURE__ */ new Map(), cadence: null }));
1638
1134
  boundary.strengths.set(signal, Math.max(_nullishCoalesce(boundary.strengths.get(signal), () => ( 0)), strength));
@@ -1646,7 +1142,7 @@ function readBoundary(boundary) {
1646
1142
  (a, b) => b[1] - a[1] || SIGNAL_ORDER.indexOf(a[0]) - SIGNAL_ORDER.indexOf(b[0])
1647
1143
  );
1648
1144
  return {
1649
- strength: _chunkQ6R3QAZDcjs.combineEvidence.call(void 0, signals.map(([, strength]) => strength)),
1145
+ strength: _chunkIFZDWIKMcjs.combineEvidence.call(void 0, signals.map(([, strength]) => strength)),
1650
1146
  signals: signals.map(([signal]) => signal)
1651
1147
  };
1652
1148
  }
@@ -1667,7 +1163,7 @@ function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
1667
1163
  continue;
1668
1164
  }
1669
1165
  const length = endBeat - (_nullishCoalesce(beats[i], () => ( 0)));
1670
- if (length < minPhraseBeats - _chunkXKSO2V5Icjs.BEAT_EPS) {
1166
+ if (length < minPhraseBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
1671
1167
  continue;
1672
1168
  }
1673
1169
  const gain = (_nullishCoalesce(value[i], () => ( 0))) + (_nullishCoalesce(strengths[j], () => ( 0))) + lengthFit(length, expected) - PHRASE_CUT_COST;
@@ -1690,43 +1186,32 @@ function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
1690
1186
  return path;
1691
1187
  }
1692
1188
  function arrivalEnd(timeline, atBeat) {
1693
- return _nullishCoalesce(_optionalChain([_chunkQ6R3QAZDcjs.segmentStartingAt.call(void 0, timeline.segments, atBeat), 'optionalAccess', _93 => _93.endBeat]), () => ( atBeat));
1694
- }
1695
- function barSpanOf(startBeat, endBeat, meters) {
1696
- const firstBar = _chunkXKSO2V5Icjs.barIndexAt.call(void 0, startBeat, meters);
1697
- const lastBar = _chunkXKSO2V5Icjs.barIndexAt.call(void 0, endBeat, meters);
1698
- const partOf = (beat, bar) => {
1699
- const barStart = _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
1700
- const barLength = _chunkXKSO2V5Icjs.beatsPerBarAt.call(void 0, barStart, meters);
1701
- return barLength > 0 ? (beat - barStart) / barLength : 0;
1702
- };
1703
- const bars = lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);
1704
- return Math.round(bars * 100) / 100;
1189
+ return _nullishCoalesce(_optionalChain([_chunkIFZDWIKMcjs.segmentStartingAt.call(void 0, timeline.segments, atBeat), 'optionalAccess', _43 => _43.endBeat]), () => ( atBeat));
1705
1190
  }
1706
1191
  function keyContextFor(opts, notes, meters, totalBeats) {
1707
1192
  if (opts.key !== void 0) {
1708
1193
  return opts.key;
1709
1194
  }
1710
- const regions = _chunkQ6R3QAZDcjs.keyTimelineFromNotes.call(void 0, notes, {
1195
+ const regions = _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, notes, {
1711
1196
  meters,
1712
1197
  totalBeats,
1713
1198
  budget: opts.budget
1714
1199
  });
1715
- const keyAt = _chunkQ6R3QAZDcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkQ6R3QAZDcjs.prevailingKeyOf.call(void 0, regions), () => ( _chunkYPB3A3EYcjs.resolveKey.call(void 0, _chunkYPB3A3EYcjs.majorKey.call(void 0, 0)))));
1716
- return (beat) => _chunkYPB3A3EYcjs.scaleOf.call(void 0, keyAt(beat));
1200
+ const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, regions), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))));
1201
+ return (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
1717
1202
  }
1718
1203
  function restBoundaries(boundaries, notes, meters, restBeats) {
1719
1204
  const ordered = [...notes].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
1720
1205
  let covered = Number.NEGATIVE_INFINITY;
1721
1206
  for (const note of ordered) {
1722
1207
  const gap = note.startBeat - covered;
1723
- if (Number.isFinite(covered) && gap >= restBeats - _chunkXKSO2V5Icjs.BEAT_EPS) {
1724
- const bar = _chunkXKSO2V5Icjs.beatsPerBarAt.call(void 0, note.startBeat, meters);
1208
+ if (Number.isFinite(covered) && gap >= restBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
1209
+ const bar = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, note.startBeat, meters);
1725
1210
  addSignal(
1726
1211
  boundaries,
1727
1212
  note.startBeat,
1728
1213
  "rest",
1729
- REST_STRENGTH * _chunkQ6R3QAZDcjs.clamp01.call(void 0, bar > 0 ? gap / bar : 1)
1214
+ REST_STRENGTH * _chunkIFZDWIKMcjs.clamp01.call(void 0, bar > 0 ? gap / bar : 1)
1730
1215
  );
1731
1216
  }
1732
1217
  covered = Math.max(covered, note.startBeat + note.durationBeat);
@@ -1736,13 +1221,13 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
1736
1221
  if (!(windowBeats > 0)) {
1737
1222
  return;
1738
1223
  }
1739
- const slices = _chunkQ6R3QAZDcjs.sliceBars.call(void 0, notes, meters, spanStart, spanEnd, budget);
1740
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, slices.length * notes.length, "form repetition comparisons", budget);
1741
- const onsetsIn = (from, to) => notes.filter((note) => note.startBeat >= from - _chunkXKSO2V5Icjs.BEAT_EPS && note.startBeat < to - _chunkXKSO2V5Icjs.BEAT_EPS);
1224
+ const slices = _chunkIFZDWIKMcjs.sliceBars.call(void 0, notes, meters, spanStart, spanEnd, budget);
1225
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slices.length * notes.length, "form repetition comparisons", budget);
1226
+ const onsetsIn = (from, to) => notes.filter((note) => note.startBeat >= from - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < to - _chunkS5BKZZGMcjs.BEAT_EPS);
1742
1227
  let comparisons = 0;
1743
1228
  for (const slice of slices) {
1744
1229
  const beat = slice.startBeat;
1745
- if (beat - windowBeats < spanStart - _chunkXKSO2V5Icjs.BEAT_EPS || beat + windowBeats > spanEnd + _chunkXKSO2V5Icjs.BEAT_EPS) {
1230
+ if (beat - windowBeats < spanStart - _chunkS5BKZZGMcjs.BEAT_EPS || beat + windowBeats > spanEnd + _chunkS5BKZZGMcjs.BEAT_EPS) {
1746
1231
  continue;
1747
1232
  }
1748
1233
  const before = onsetsIn(beat - windowBeats, beat);
@@ -1751,8 +1236,8 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
1751
1236
  continue;
1752
1237
  }
1753
1238
  comparisons += before.length * after.length;
1754
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, comparisons, "form repetition melody comparisons", budget);
1755
- const similarity = melodicSimilarity(before, after);
1239
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "form repetition melody comparisons", budget);
1240
+ const similarity = _chunkIFZDWIKMcjs.melodicSimilarity.call(void 0, before, after);
1756
1241
  if (similarity >= REPETITION_THRESHOLD) {
1757
1242
  addSignal(boundaries, beat, "repetition", REPETITION_STRENGTH * similarity);
1758
1243
  }
@@ -1781,7 +1266,7 @@ function structuralWeightOf(cadence, confidence, aligned, isLast) {
1781
1266
  if (aligned) {
1782
1267
  weight += HYPERMETRIC_CLOSE_BONUS;
1783
1268
  }
1784
- return _chunkQ6R3QAZDcjs.clamp01.call(void 0, weight);
1269
+ return _chunkIFZDWIKMcjs.clamp01.call(void 0, weight);
1785
1270
  }
1786
1271
  function describeCadence(cadence) {
1787
1272
  if (cadence === null || cadence.cadence.type === null) {
@@ -1792,15 +1277,15 @@ function describeCadence(cadence) {
1792
1277
  return `a ${graded} cadence at beat ${cadence.atBeat}`;
1793
1278
  }
1794
1279
  function phrasesFromTimeline(timeline, notes, opts = {}) {
1795
- _chunkQ6R3QAZDcjs.assertChordTimeline.call(void 0, timeline);
1796
- const meters = _chunkXKSO2V5Icjs.resolveMeters.call(void 0, opts, "phrase meters");
1797
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, "phrase notes", {
1280
+ _chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, timeline);
1281
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "phrase meters");
1282
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "phrase notes", {
1798
1283
  allowNonPositiveDuration: true,
1799
1284
  budget: opts.budget
1800
1285
  });
1801
1286
  if (opts.hypermeter !== void 0) {
1802
- _chunkXKSO2V5Icjs.assertRange.call(void 0, opts.hypermeter.confidence, 0, 1, "phrase hypermeter confidence");
1803
- _chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, opts.hypermeter.groupBars, "phrase hypermeter groupBars");
1287
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, opts.hypermeter.confidence, 0, 1, "phrase hypermeter confidence");
1288
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, opts.hypermeter.groupBars, "phrase hypermeter groupBars");
1804
1289
  }
1805
1290
  const sounding = notes.filter((note) => note.durationBeat > 0);
1806
1291
  const segments = timeline.segments;
@@ -1808,39 +1293,39 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1808
1293
  const lastSegment = segments[segments.length - 1];
1809
1294
  const firstOnset = sounding.reduce(
1810
1295
  (first, n) => Math.min(first, n.startBeat),
1811
- _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _94 => _94.startBeat]), () => ( 0))
1296
+ _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _44 => _44.startBeat]), () => ( 0))
1812
1297
  );
1813
- const spanStart = _chunkQ6R3QAZDcjs.barGridStart.call(void 0, firstOnset, meters);
1298
+ const spanStart = _chunkIFZDWIKMcjs.barGridStart.call(void 0, firstOnset, meters);
1814
1299
  const notesEnd = sounding.reduce(
1815
1300
  (end, n) => Math.max(end, n.startBeat + n.durationBeat),
1816
- _nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _95 => _95.endBeat]), () => ( 0))
1301
+ _nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _45 => _45.endBeat]), () => ( 0))
1817
1302
  );
1818
1303
  const spanEnd = Math.max(spanStart, _nullishCoalesce(opts.totalBeats, () => ( notesEnd)));
1819
- _chunkXKSO2V5Icjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "phrase totalBeats");
1820
- if (spanEnd <= spanStart + _chunkXKSO2V5Icjs.BEAT_EPS) {
1304
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "phrase totalBeats");
1305
+ if (spanEnd <= spanStart + _chunkS5BKZZGMcjs.BEAT_EPS) {
1821
1306
  return [];
1822
1307
  }
1823
- const barBeats = _chunkXKSO2V5Icjs.beatsPerBarAt.call(void 0, Math.max(0, spanStart), meters);
1308
+ const barBeats = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, Math.max(0, spanStart), meters);
1824
1309
  const restBeats = _nullishCoalesce(opts.restBeats, () => ( barBeats / 2));
1825
- _chunkXKSO2V5Icjs.assertRange.call(void 0, restBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase restBeats");
1310
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, restBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase restBeats");
1826
1311
  const longNoteBeats = _nullishCoalesce(opts.longNoteBeats, () => ( barBeats));
1827
- _chunkXKSO2V5Icjs.assertRange.call(void 0, longNoteBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase longNoteBeats");
1312
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, longNoteBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase longNoteBeats");
1828
1313
  const minPhraseBeats = _nullishCoalesce(opts.minPhraseBeats, () => ( barBeats));
1829
- _chunkXKSO2V5Icjs.assertRange.call(void 0, minPhraseBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase minPhraseBeats");
1314
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, minPhraseBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase minPhraseBeats");
1830
1315
  const key = keyContextFor(opts, sounding, meters, spanEnd);
1831
- const cadences = _chunkQ6R3QAZDcjs.detectCadences.call(void 0, timeline, key);
1316
+ const cadences = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, key);
1832
1317
  const grouping = _nullishCoalesce(opts.hypermeter, () => ( hypermeter(sounding, meters, {
1833
1318
  totalBeats: spanEnd,
1834
1319
  cadenceBeats: cadences.map((hit) => hit.atBeat),
1835
1320
  budget: opts.budget
1836
1321
  })));
1837
- const firstBar = _chunkXKSO2V5Icjs.barIndexAt.call(void 0, Math.max(0, spanStart), meters);
1322
+ const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(0, spanStart), meters);
1838
1323
  const hyperBeats = Math.max(
1839
1324
  barBeats,
1840
- _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar: firstBar + grouping.groupBars, beat: 0 }, meters) - _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar: firstBar, beat: 0 }, meters)
1325
+ _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: firstBar + grouping.groupBars, beat: 0 }, meters) - _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: firstBar, beat: 0 }, meters)
1841
1326
  );
1842
1327
  const expected = _nullishCoalesce(opts.expectedPhraseBeats, () => ( hyperBeats));
1843
- _chunkXKSO2V5Icjs.assertRange.call(void 0, expected, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase expectedPhraseBeats");
1328
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, expected, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase expectedPhraseBeats");
1844
1329
  const boundaries = /* @__PURE__ */ new Map();
1845
1330
  for (const hit of cadences) {
1846
1331
  const type = hit.cadence.type;
@@ -1850,14 +1335,14 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1850
1335
  const strength = CADENCE_BOUNDARY_STRENGTH[type];
1851
1336
  const chordEnd = arrivalEnd(timeline, hit.atBeat);
1852
1337
  addSignal(boundaries, chordEnd, "cadence", strength, hit);
1853
- const barEnd = _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar: _chunkXKSO2V5Icjs.barIndexAt.call(void 0, hit.atBeat, meters) + 1, beat: 0 }, meters);
1854
- if (chordEnd > barEnd + _chunkXKSO2V5Icjs.BEAT_EPS) {
1338
+ const barEnd = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: _chunkS5BKZZGMcjs.barIndexAt.call(void 0, hit.atBeat, meters) + 1, beat: 0 }, meters);
1339
+ if (chordEnd > barEnd + _chunkS5BKZZGMcjs.BEAT_EPS) {
1855
1340
  addSignal(boundaries, barEnd, "cadence", strength, hit);
1856
1341
  }
1857
1342
  }
1858
1343
  restBoundaries(boundaries, sounding, meters, restBeats);
1859
1344
  for (const note of sounding) {
1860
- if (note.durationBeat >= longNoteBeats - _chunkXKSO2V5Icjs.BEAT_EPS) {
1345
+ if (note.durationBeat >= longNoteBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
1861
1346
  addSignal(boundaries, note.startBeat + note.durationBeat, "longNote", LONG_NOTE_STRENGTH);
1862
1347
  }
1863
1348
  }
@@ -1867,9 +1352,9 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1867
1352
  }
1868
1353
  addSignal(boundaries, spanEnd, "pieceEnd", PIECE_END_STRENGTH);
1869
1354
  const candidates = [...boundaries.values()].filter(
1870
- (boundary) => boundary.beat > spanStart + _chunkXKSO2V5Icjs.BEAT_EPS && boundary.beat < spanEnd - _chunkXKSO2V5Icjs.BEAT_EPS
1355
+ (boundary) => boundary.beat > spanStart + _chunkS5BKZZGMcjs.BEAT_EPS && boundary.beat < spanEnd - _chunkS5BKZZGMcjs.BEAT_EPS
1871
1356
  ).filter((boundary) => readBoundary(boundary).strength > 0).sort((a, b) => a.beat - b.beat);
1872
- const endBoundary = _nullishCoalesce(boundaries.get(Math.round(spanEnd / _chunkXKSO2V5Icjs.BEAT_EPS)), () => ( {
1357
+ const endBoundary = _nullishCoalesce(boundaries.get(Math.round(spanEnd / _chunkS5BKZZGMcjs.BEAT_EPS)), () => ( {
1873
1358
  beat: spanEnd,
1874
1359
  strengths: /* @__PURE__ */ new Map(),
1875
1360
  cadence: null
@@ -1879,7 +1364,7 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1879
1364
  ...candidates,
1880
1365
  endBoundary
1881
1366
  ];
1882
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, stops.length * stops.length, "form phrase boundary pairs", opts.budget);
1367
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, stops.length * stops.length, "form phrase boundary pairs", opts.budget);
1883
1368
  const readings = stops.map(readBoundary);
1884
1369
  const path = choosePhrasePath(
1885
1370
  stops.map((stop) => stop.beat),
@@ -1888,31 +1373,31 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1888
1373
  expected
1889
1374
  );
1890
1375
  const downbeatKeys = new Set(
1891
- grouping.downbeats.map((downbeat) => Math.round(downbeat / _chunkXKSO2V5Icjs.BEAT_EPS))
1376
+ grouping.downbeats.map((downbeat) => Math.round(downbeat / _chunkS5BKZZGMcjs.BEAT_EPS))
1892
1377
  );
1893
1378
  const phrases = [];
1894
1379
  let from = 0;
1895
1380
  for (let n = 0; n < path.length; n += 1) {
1896
1381
  const to = _nullishCoalesce(path[n], () => ( stops.length - 1));
1897
- const startBeat = _nullishCoalesce(_optionalChain([stops, 'access', _96 => _96[from], 'optionalAccess', _97 => _97.beat]), () => ( spanStart));
1382
+ const startBeat = _nullishCoalesce(_optionalChain([stops, 'access', _46 => _46[from], 'optionalAccess', _47 => _47.beat]), () => ( spanStart));
1898
1383
  const stop = stops[to];
1899
- const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _98 => _98.beat]), () => ( spanEnd));
1384
+ const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _48 => _48.beat]), () => ( spanEnd));
1900
1385
  const reading = _nullishCoalesce(readings[to], () => ( { strength: 0, signals: [] }));
1901
1386
  const length = endBeat - startBeat;
1902
- const confidence = _chunkQ6R3QAZDcjs.clamp01.call(void 0,
1387
+ const confidence = _chunkIFZDWIKMcjs.clamp01.call(void 0,
1903
1388
  CLOSING_SHARE * reading.strength + (1 - CLOSING_SHARE) * Math.max(0, lengthFit(length, expected))
1904
1389
  );
1905
1390
  const isLast = n === path.length - 1;
1906
- const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _99 => _99.cadence]), () => ( null));
1907
- const cadence = closing !== null && closing.atBeat >= startBeat - _chunkXKSO2V5Icjs.BEAT_EPS && closing.atBeat < endBeat - _chunkXKSO2V5Icjs.BEAT_EPS ? closing : null;
1391
+ const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _49 => _49.cadence]), () => ( null));
1392
+ const cadence = closing !== null && closing.atBeat >= startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && closing.atBeat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS ? closing : null;
1908
1393
  const structuralWeight = structuralWeightOf(
1909
1394
  cadence,
1910
1395
  confidence,
1911
- downbeatKeys.has(Math.round(endBeat / _chunkXKSO2V5Icjs.BEAT_EPS)),
1396
+ downbeatKeys.has(Math.round(endBeat / _chunkS5BKZZGMcjs.BEAT_EPS)),
1912
1397
  isLast
1913
1398
  );
1914
1399
  const signals = cadence === null ? reading.signals.filter((signal) => signal !== "cadence") : reading.signals;
1915
- const bars = barSpanOf(startBeat, endBeat, meters);
1400
+ const bars = Math.round(_chunkIFZDWIKMcjs.barSpanOf.call(void 0, startBeat, endBeat, meters) * 100) / 100;
1916
1401
  phrases.push({
1917
1402
  startBeat,
1918
1403
  endBeat,
@@ -1927,10 +1412,10 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
1927
1412
  return phrases;
1928
1413
  }
1929
1414
  function structuralCadences(phrases) {
1930
- _chunkXKSO2V5Icjs.assertArray.call(void 0, phrases, "phrases");
1415
+ _chunkS5BKZZGMcjs.assertArray.call(void 0, phrases, "phrases");
1931
1416
  const ranked = [];
1932
1417
  for (let index = 0; index < phrases.length; index += 1) {
1933
- const phrase = _chunkXKSO2V5Icjs.assertRecord.call(void 0, phrases[index], `phrases[${index}]`);
1418
+ const phrase = _chunkS5BKZZGMcjs.assertRecord.call(void 0, phrases[index], `phrases[${index}]`);
1934
1419
  if (phrase.cadence === null || phrase.cadence === void 0 || phrase.cadence.cadence.type === null) {
1935
1420
  continue;
1936
1421
  }
@@ -1964,16 +1449,16 @@ function isSilent(unit) {
1964
1449
  return unit.notes.length === 0;
1965
1450
  }
1966
1451
  function unitSimilarity(a, b) {
1967
- const harmony = _chunkQ6R3QAZDcjs.weightSimilarity.call(void 0, a.weights, b.weights);
1452
+ const harmony = _chunkIFZDWIKMcjs.weightSimilarity.call(void 0, a.weights, b.weights);
1968
1453
  if (a.notes.length < 2 || b.notes.length < 2) {
1969
1454
  return a.notes.length === b.notes.length ? harmony : 0;
1970
1455
  }
1971
- const melody = melodicSimilarity(a.notes, b.notes);
1972
- return _chunkQ6R3QAZDcjs.clamp01.call(void 0, MELODY_SHARE * melody + HARMONY_SHARE * harmony);
1456
+ const melody = _chunkIFZDWIKMcjs.melodicSimilarity.call(void 0, a.notes, b.notes);
1457
+ return _chunkIFZDWIKMcjs.clamp01.call(void 0, MELODY_SHARE * melody + HARMONY_SHARE * harmony);
1973
1458
  }
1974
1459
  function sectionsFromNotes(notes, opts = {}) {
1975
- const meters = _chunkXKSO2V5Icjs.resolveMeters.call(void 0, opts, "section meters");
1976
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, notes, "section notes", {
1460
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "section meters");
1461
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "section notes", {
1977
1462
  allowNonPositiveDuration: true,
1978
1463
  budget: opts.budget
1979
1464
  });
@@ -1981,33 +1466,33 @@ function sectionsFromNotes(notes, opts = {}) {
1981
1466
  if (sounding.length === 0) {
1982
1467
  return [];
1983
1468
  }
1984
- const firstOnset = _chunkQ6R3QAZDcjs.barGridStart.call(void 0, _chunkQ6R3QAZDcjs.firstSoundingBeat.call(void 0, sounding), meters);
1469
+ const firstOnset = _chunkIFZDWIKMcjs.barGridStart.call(void 0, _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding), meters);
1985
1470
  const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
1986
1471
  const spanEnd = Math.max(firstOnset, _nullishCoalesce(opts.totalBeats, () => ( lastEnd)));
1987
- _chunkXKSO2V5Icjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "section totalBeats");
1472
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "section totalBeats");
1988
1473
  const threshold = _nullishCoalesce(opts.threshold, () => ( DEFAULT_THRESHOLD));
1989
- _chunkXKSO2V5Icjs.assertRange.call(void 0, threshold, 0, 1, "section threshold");
1474
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, threshold, 0, 1, "section threshold");
1990
1475
  const unitBars = _nullishCoalesce(opts.unitBars, () => ( hypermeter(sounding, meters, { totalBeats: spanEnd, budget: opts.budget }).groupBars));
1991
- _chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, unitBars, "section unitBars");
1992
- const firstBar = _chunkXKSO2V5Icjs.barIndexAt.call(void 0, firstOnset, meters);
1993
- const lastBar = _chunkQ6R3QAZDcjs.lastBarOf.call(void 0, meters, firstOnset, spanEnd);
1476
+ _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, unitBars, "section unitBars");
1477
+ const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, firstOnset, meters);
1478
+ const lastBar = _chunkIFZDWIKMcjs.lastBarOf.call(void 0, meters, firstOnset, spanEnd);
1994
1479
  const startBar = Math.max(0, firstBar);
1995
1480
  const unitCount = Math.max(1, Math.ceil((lastBar - startBar + 1) / unitBars));
1996
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, unitCount * unitCount, "form section comparisons", opts.budget);
1997
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, unitCount * sounding.length, "form section unit notes", opts.budget);
1481
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, unitCount * unitCount, "form section comparisons", opts.budget);
1482
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, unitCount * sounding.length, "form section unit notes", opts.budget);
1998
1483
  const units = [];
1999
1484
  for (let index = 0; index < unitCount; index += 1) {
2000
1485
  const barStart = startBar + index * unitBars;
2001
- const startBeat = index === 0 ? firstOnset : _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar: barStart, beat: 0 }, meters);
2002
- const endBeat = index === unitCount - 1 ? spanEnd : _chunkXKSO2V5Icjs.barPositionToBeat.call(void 0, { bar: barStart + unitBars, beat: 0 }, meters);
1486
+ const startBeat = index === 0 ? firstOnset : _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: barStart, beat: 0 }, meters);
1487
+ const endBeat = index === unitCount - 1 ? spanEnd : _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: barStart + unitBars, beat: 0 }, meters);
2003
1488
  const unitNotes = sounding.filter(
2004
- (note) => note.startBeat >= startBeat - _chunkXKSO2V5Icjs.BEAT_EPS && note.startBeat < endBeat - _chunkXKSO2V5Icjs.BEAT_EPS
1489
+ (note) => note.startBeat >= startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS
2005
1490
  ).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
2006
1491
  units.push({
2007
1492
  startBeat,
2008
1493
  endBeat,
2009
1494
  notes: unitNotes,
2010
- weights: _chunkQ6R3QAZDcjs.windowWeights.call(void 0, unitNotes, startBeat, endBeat, meters).weights
1495
+ weights: _chunkIFZDWIKMcjs.windowWeights.call(void 0, unitNotes, startBeat, endBeat, meters).weights
2011
1496
  });
2012
1497
  }
2013
1498
  let comparisons = 0;
@@ -2016,7 +1501,7 @@ function sectionsFromNotes(notes, opts = {}) {
2016
1501
  comparisons += unit.notes.length * earlierNotes;
2017
1502
  earlierNotes += unit.notes.length;
2018
1503
  }
2019
- _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, comparisons, "form section melody comparisons", opts.budget);
1504
+ _chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "form section melody comparisons", opts.budget);
2020
1505
  const labels = [];
2021
1506
  const scores = [];
2022
1507
  let distinct = 0;
@@ -2071,8 +1556,8 @@ function sectionsFromNotes(notes, opts = {}) {
2071
1556
  while (end < units.length && labels[end] === null) {
2072
1557
  end += 1;
2073
1558
  }
2074
- const startBeat = _nullishCoalesce(_optionalChain([units, 'access', _100 => _100[index], 'optionalAccess', _101 => _101.startBeat]), () => ( 0));
2075
- const endBeat = _nullishCoalesce(_optionalChain([units, 'access', _102 => _102[end - 1], 'optionalAccess', _103 => _103.endBeat]), () => ( spanEnd));
1559
+ const startBeat = _nullishCoalesce(_optionalChain([units, 'access', _50 => _50[index], 'optionalAccess', _51 => _51.startBeat]), () => ( 0));
1560
+ const endBeat = _nullishCoalesce(_optionalChain([units, 'access', _52 => _52[end - 1], 'optionalAccess', _53 => _53.endBeat]), () => ( spanEnd));
2076
1561
  const sectionFirstBar = startBar + index * unitBars;
2077
1562
  const sectionLastBar = Math.max(
2078
1563
  sectionFirstBar,
@@ -2083,7 +1568,7 @@ function sectionsFromNotes(notes, opts = {}) {
2083
1568
  if (first === void 0) {
2084
1569
  firstByLabel.set(label, sectionIndex);
2085
1570
  }
2086
- const similarity = first === void 0 ? 1 : _chunkQ6R3QAZDcjs.clamp01.call(void 0, scoreSum / Math.max(1, sounded));
1571
+ const similarity = first === void 0 ? 1 : _chunkIFZDWIKMcjs.clamp01.call(void 0, scoreSum / Math.max(1, sounded));
2087
1572
  sections.push({
2088
1573
  label,
2089
1574
  startBeat,
@@ -2091,163 +1576,284 @@ function sectionsFromNotes(notes, opts = {}) {
2091
1576
  bars: sectionLastBar - sectionFirstBar + 1,
2092
1577
  firstOccurrence: _nullishCoalesce(first, () => ( sectionIndex)),
2093
1578
  similarity,
2094
- rationale: first === void 0 ? `Section ${label}: material first heard at beat ${startBeat}` : `Section ${label}: ${Math.round(similarity * 100)}% alike the ${label} at beat ${_nullishCoalesce(_optionalChain([sections, 'access', _104 => _104[first], 'optionalAccess', _105 => _105.startBeat]), () => ( 0))}`
1579
+ rationale: first === void 0 ? `Section ${label}: material first heard at beat ${startBeat}` : `Section ${label}: ${Math.round(similarity * 100)}% alike the ${label} at beat ${_nullishCoalesce(_optionalChain([sections, 'access', _54 => _54[first], 'optionalAccess', _55 => _55.startBeat]), () => ( 0))}`
2095
1580
  });
2096
1581
  index = end;
2097
1582
  }
2098
1583
  return sections;
2099
1584
  }
2100
1585
 
2101
- // src/analyze/reduction/index.ts
2102
- var TONIC_ARRIVING = /* @__PURE__ */ new Set(["authentic", "plagal", "modal", "deceptive"]);
2103
- function signedStep(fromPc, toPc) {
2104
- return (_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, toPc) - _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, fromPc) + 18) % 12 - 6;
1586
+ // src/analyze/reference/compare.ts
1587
+ var RATIONALE_FIELDS = 2;
1588
+ function rationaleOf(comparison) {
1589
+ const measured = [];
1590
+ for (const group of [comparison.form, comparison.harmony, comparison.melody, comparison.rhythm]) {
1591
+ for (const [name, value] of Object.entries(group)) {
1592
+ if (value !== null) {
1593
+ measured.push({ name, value, order: measured.length });
1594
+ }
1595
+ }
1596
+ }
1597
+ if (measured.length === 0) {
1598
+ return "Nothing to compare: neither profile has material for any field";
1599
+ }
1600
+ const describe = (entries) => entries.map((entry) => `${entry.name} ${Math.round(entry.value * 100)}%`).join(", ");
1601
+ const highest = [...measured].sort((x, y) => y.value - x.value || x.order - y.order);
1602
+ const lowest = [...measured].sort((x, y) => x.value - y.value || x.order - y.order);
1603
+ return `Most alike: ${describe(highest.slice(0, RATIONALE_FIELDS))}; least alike: ${describe(lowest.slice(0, RATIONALE_FIELDS))}`;
1604
+ }
1605
+ function compareReferences(a, b, opts) {
1606
+ _chunkIFZDWIKMcjs.assertReferenceProfile.call(void 0, a, "reference profile a");
1607
+ _chunkIFZDWIKMcjs.assertReferenceProfile.call(void 0, b, "reference profile b");
1608
+ const { budget } = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "compare options");
1609
+ const structural = (profile) => profile.harmony.chords.filter((chord) => chord.level === "structural");
1610
+ const cadences = (profile) => profile.form.phrases.map((phrase) => _nullishCoalesce(_optionalChain([phrase, 'access', _56 => _56.cadence, 'optionalAccess', _57 => _57.type]), () => ( null)));
1611
+ const phraseMelodies = (profile) => profile.form.phrases.map((phrase) => phrase.melody);
1612
+ const comparison = {
1613
+ form: {
1614
+ sectionSequenceSimilarity: _chunkIFZDWIKMcjs.sectionSequenceSimilarity.call(void 0,
1615
+ a.form.sections.map((section) => section.label),
1616
+ b.form.sections.map((section) => section.label)
1617
+ ),
1618
+ phraseLengthSimilarity: _chunkIFZDWIKMcjs.phraseLengthSimilarity.call(void 0,
1619
+ a.form.phrases.map((phrase) => phrase.bars),
1620
+ b.form.phrases.map((phrase) => phrase.bars)
1621
+ )
1622
+ },
1623
+ harmony: {
1624
+ progressionSimilarity: _chunkIFZDWIKMcjs.progressionSimilarity.call(void 0, structural(a), structural(b), budget),
1625
+ functionSimilarity: _chunkIFZDWIKMcjs.functionSimilarity.call(void 0, a.harmony.chords, b.harmony.chords),
1626
+ cadenceSimilarity: _chunkIFZDWIKMcjs.cadenceSimilarity.call(void 0, cadences(a), cadences(b)),
1627
+ keyPlanSimilarity: _chunkIFZDWIKMcjs.keyPlanSimilarity.call(void 0,
1628
+ a.harmony.keys.map((region) => region.key),
1629
+ b.harmony.keys.map((region) => region.key)
1630
+ ),
1631
+ harmonicRhythmOnsetSimilarity: _chunkIFZDWIKMcjs.onsetSimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm),
1632
+ harmonicRhythmDensitySimilarity: _chunkIFZDWIKMcjs.densitySimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm)
1633
+ },
1634
+ melody: {
1635
+ surfaceSimilarity: _chunkIFZDWIKMcjs.surfaceSimilarity.call(void 0, a.melody.motifs, b.melody.motifs),
1636
+ motifStructureSimilarity: _chunkIFZDWIKMcjs.motifStructureSimilarity.call(void 0,
1637
+ { graph: a.melody.graph, spanBeats: a.span.endBeat - a.span.startBeat },
1638
+ { graph: b.melody.graph, spanBeats: b.span.endBeat - b.span.startBeat }
1639
+ ),
1640
+ contourSimilarity: _chunkIFZDWIKMcjs.contourSimilarity.call(void 0, phraseMelodies(a), phraseMelodies(b)),
1641
+ registerSimilarity: _chunkIFZDWIKMcjs.registerSimilarity.call(void 0,
1642
+ { phrases: phraseMelodies(a), mean: _nullishCoalesce(_optionalChain([a, 'access', _58 => _58.melody, 'access', _59 => _59.register, 'optionalAccess', _60 => _60.mean]), () => ( null)) },
1643
+ { phrases: phraseMelodies(b), mean: _nullishCoalesce(_optionalChain([b, 'access', _61 => _61.melody, 'access', _62 => _62.register, 'optionalAccess', _63 => _63.mean]), () => ( null)) }
1644
+ )
1645
+ },
1646
+ rhythm: {
1647
+ onsetSimilarity: _chunkIFZDWIKMcjs.onsetSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
1648
+ densitySimilarity: _chunkIFZDWIKMcjs.densitySimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
1649
+ durationSimilarity: _chunkIFZDWIKMcjs.durationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
1650
+ syncopationSimilarity: _chunkIFZDWIKMcjs.syncopationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm)
1651
+ }
1652
+ };
1653
+ return { ...comparison, rationale: rationaleOf(comparison) };
2105
1654
  }
2106
- function isStep(distance) {
2107
- const size = Math.abs(distance);
2108
- return size === 1 || size === 2;
1655
+
1656
+ // src/analyze/reference/profile.ts
1657
+ function spanOf(sounding, timeline, meters, totalBeats) {
1658
+ const segments = timeline.segments;
1659
+ const firstSegment = segments[0];
1660
+ const lastSegment = segments[segments.length - 1];
1661
+ const firstOnset = _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _64 => _64.startBeat]), () => ( 0)));
1662
+ const startBeat = _chunkIFZDWIKMcjs.barGridStart.call(void 0,
1663
+ Math.min(firstOnset, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _65 => _65.startBeat]), () => ( firstOnset))),
1664
+ meters
1665
+ );
1666
+ const notesEnd = sounding.reduce(
1667
+ (end, note) => Math.max(end, note.startBeat + note.durationBeat),
1668
+ _nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _66 => _66.endBeat]), () => ( 0))
1669
+ );
1670
+ return { startBeat, endBeat: Math.max(startBeat, _nullishCoalesce(totalBeats, () => ( notesEnd))) };
1671
+ }
1672
+ function sameKey(a, b) {
1673
+ return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, a.scale.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, b.scale.rootPc) && a.scale.modeMask12 === b.scale.modeMask12;
2109
1674
  }
2110
- function touches(before, after) {
2111
- return Math.abs(after.startBeat - before.endBeat) <= _chunkXKSO2V5Icjs.BEAT_EPS;
1675
+ function keyIndexAt(regions, beat, prevailing) {
1676
+ const holding = regions.findIndex((region) => region.startBeat <= beat && beat < region.endBeat);
1677
+ if (holding >= 0) {
1678
+ return holding;
1679
+ }
1680
+ return Math.max(
1681
+ 0,
1682
+ regions.findIndex((region) => sameKey(region.key, prevailing))
1683
+ );
2112
1684
  }
2113
- function salienceFloors(durations) {
2114
- const order = durations.map((_, index) => index).sort((a, b) => {
2115
- const left = _nullishCoalesce(durations[a], () => ( 0));
2116
- const right = _nullishCoalesce(durations[b], () => ( 0));
2117
- return left - right || a - b;
1685
+ function sectionAt(sections, beat) {
1686
+ const index = sections.findIndex(
1687
+ (section) => beat >= section.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && beat < section.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS
1688
+ );
1689
+ return index < 0 ? null : index;
1690
+ }
1691
+ function readingsFor(notes, opts = {}) {
1692
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "reference options");
1693
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, asked, "reference meters");
1694
+ const budget = asked.budget;
1695
+ if (asked.timeline === void 0) {
1696
+ return _chunkIFZDWIKMcjs.chordTimelineFromNotes.call(void 0, notes, {
1697
+ meters,
1698
+ key: asked.key,
1699
+ totalBeats: asked.totalBeats,
1700
+ budget
1701
+ });
1702
+ }
1703
+ const timeline = _chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, asked.timeline, "reference timeline");
1704
+ let keys;
1705
+ if (asked.key === void 0) {
1706
+ keys = _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, notes, { meters, totalBeats: asked.totalBeats, budget });
1707
+ } else {
1708
+ const sounding = _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
1709
+ allowNonPositiveDuration: true,
1710
+ budget
1711
+ }).filter((note) => note.durationBeat > 0);
1712
+ const span = spanOf(sounding, timeline, meters, asked.totalBeats);
1713
+ keys = [
1714
+ {
1715
+ startBeat: span.startBeat,
1716
+ endBeat: span.endBeat,
1717
+ key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, asked.key),
1718
+ confidence: 1
1719
+ }
1720
+ ];
1721
+ }
1722
+ return { timeline, keys, prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))) };
1723
+ }
1724
+ function referenceFromReadings(notes, readings, opts = {}) {
1725
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "reference options");
1726
+ const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, asked, "reference meters");
1727
+ const budget = asked.budget;
1728
+ const sounding = _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
1729
+ allowNonPositiveDuration: true,
1730
+ budget
1731
+ }).filter((note) => note.durationBeat > 0);
1732
+ if (asked.totalBeats !== void 0) {
1733
+ _chunkS5BKZZGMcjs.assertRange.call(void 0, asked.totalBeats, 0, Number.MAX_SAFE_INTEGER, "reference totalBeats");
1734
+ }
1735
+ const line = _chunkIFZDWIKMcjs.orderedNotes.call(void 0, _nullishCoalesce(asked.melody, () => ( notes)), "reference melody", budget);
1736
+ const { timeline, prevailingKey } = readings;
1737
+ const span = spanOf(sounding, timeline, meters, asked.totalBeats);
1738
+ const spanEnd = span.endBeat;
1739
+ const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, readings.keys, prevailingKey);
1740
+ const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
1741
+ const cadenceBeats = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, scaleAt).map((hit) => hit.atBeat);
1742
+ const grouping = hypermeter(sounding, meters, { cadenceBeats, totalBeats: spanEnd, budget });
1743
+ const phrases = phrasesFromTimeline(timeline, notes, {
1744
+ meters,
1745
+ key: scaleAt,
1746
+ hypermeter: grouping,
1747
+ totalBeats: spanEnd,
1748
+ budget
2118
1749
  });
2119
- const rankOf = new Array(durations.length).fill(0);
2120
- for (let rank = 0; rank < order.length; rank += 1) {
2121
- rankOf[_nullishCoalesce(order[rank], () => ( 0))] = rank;
2122
- }
2123
- const floors = [];
2124
- const remaining = durations.length - 1;
2125
- for (let index = 0; index < durations.length; index += 1) {
2126
- if (remaining <= 0) {
2127
- floors.push(0);
2128
- continue;
2129
- }
2130
- const skipped = _nullishCoalesce(rankOf[index], () => ( 0));
2131
- const at = (position) => _nullishCoalesce(durations[_nullishCoalesce(order[position < skipped ? position : position + 1], () => ( 0))], () => ( 0));
2132
- const middle = remaining >> 1;
2133
- floors.push(remaining % 2 === 1 ? at(middle) : (at(middle - 1) + at(middle)) / 2);
2134
- }
2135
- return floors;
2136
- }
2137
- function isTonicChord(chord, key) {
2138
- return _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 0 && !_chunkQ6R3QAZDcjs.isAppliedDominant.call(void 0, chord, key);
2139
- }
2140
- function functionalReason(segment, before, after, keyAt) {
2141
- const key = keyAt(segment.startBeat);
2142
- if (after !== void 0 && _chunkQ6R3QAZDcjs.isCadentialSixFour.call(void 0, segment.chord, after.chord, key)) {
2143
- return { kind: "cadentialSixFour" };
2144
- }
2145
- if (isTonicChord(segment.chord, key)) {
2146
- return { kind: "tonic" };
2147
- }
2148
- if (_chunkYPB3A3EYcjs.isDominantChordOf.call(void 0, segment.chord, key)) {
2149
- return { kind: "dominant" };
2150
- }
2151
- if (before !== void 0) {
2152
- const arrival = _chunkQ6R3QAZDcjs.detectCadence.call(void 0, before.chord, segment.chord, key).type;
2153
- if (arrival !== null) {
2154
- return { kind: "cadenceArrival", cadence: arrival };
2155
- }
2156
- }
2157
- if (after !== void 0) {
2158
- const agent = _chunkQ6R3QAZDcjs.detectCadence.call(void 0, segment.chord, after.chord, keyAt(after.startBeat)).type;
2159
- if (agent !== null && TONIC_ARRIVING.has(agent)) {
2160
- return { kind: "cadenceAgent", cadence: agent };
2161
- }
2162
- }
2163
- return null;
2164
- }
2165
- function figureReason(segment, before, after) {
2166
- if (before === void 0 || after === void 0) {
2167
- return null;
2168
- }
2169
- const incoming = signedStep(before.chord.rootPc, segment.chord.rootPc);
2170
- const outgoing = signedStep(segment.chord.rootPc, after.chord.rootPc);
2171
- if (!isStep(incoming) || !isStep(outgoing)) {
2172
- return null;
2173
- }
2174
- if (incoming > 0 === outgoing > 0) {
2175
- return { kind: "passing" };
2176
- }
2177
- return _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, before.chord.rootPc) === _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, after.chord.rootPc) ? { kind: "neighbor" } : null;
2178
- }
2179
- function describe(reason) {
2180
- switch (reason.kind) {
2181
- case "tonic":
2182
- return "Structural: the tonic of the key, which the progression is heard against";
2183
- case "dominant":
2184
- return "Structural: the dominant of the key, the chord its tonic is approached from";
2185
- case "cadentialSixFour":
2186
- return "Structural: a tonic six-four on the bass of the dominant that follows, which prolongs that dominant rather than framing the tonic";
2187
- case "cadenceArrival":
2188
- return `Structural: the arrival of a ${reason.cadence} cadence`;
2189
- case "cadenceAgent":
2190
- return `Structural: the chord that drives a ${reason.cadence} cadence`;
2191
- case "salient":
2192
- return "Structural: it holds the harmony longer than the chords around it";
2193
- case "first":
2194
- return "Structural: the first chord of the progression frames it";
2195
- case "last":
2196
- return "Structural: the last chord of the progression frames it";
2197
- case "unfigured":
2198
- return "Structural: its root neither steps through to the next chord nor returns to the last, so it embellishes nothing";
2199
- case "passing":
2200
- return "Passing: its root steps in from the previous chord and on to the next in the same direction";
2201
- case "neighbor":
2202
- return "Neighbor: its root steps away from the surrounding harmony and returns to it";
2203
- }
2204
- }
2205
- var BASIS_READINGS = {
2206
- function: ({ segment, before, after, keyAt }) => functionalReason(segment, before, after, keyAt),
2207
- duration: ({ segment, salienceFloor }) => segment.endBeat - segment.startBeat > salienceFloor ? { kind: "salient" } : null
2208
- };
2209
- var REDUCTION_BASES = Object.keys(BASIS_READINGS);
2210
- function levelOf(reason) {
2211
- if (reason.kind === "passing") {
2212
- return "passing";
2213
- }
2214
- return reason.kind === "neighbor" ? "neighbor" : "structural";
2215
- }
2216
- function reduceProgression(timeline, key, opts = {}) {
2217
- _chunkQ6R3QAZDcjs.assertChordTimeline.call(void 0, timeline);
2218
- const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
2219
- const keyAt = _chunkQ6R3QAZDcjs.keyScaleAt.call(void 0, key);
2220
- const basis = _chunkXKSO2V5Icjs.assertOneOf.call(void 0, _nullishCoalesce(asked.basis, () => ( "function")), REDUCTION_BASES, "reduction basis");
2221
- const reading = BASIS_READINGS[basis];
2222
- const segments = timeline.segments;
2223
- const floors = basis === "duration" ? salienceFloors(segments.map((segment) => segment.endBeat - segment.startBeat)) : [];
2224
- const result = [];
2225
- for (let index = 0; index < segments.length; index += 1) {
2226
- const segment = segments[index];
2227
- if (segment === void 0) {
2228
- continue;
1750
+ const cadences = /* @__PURE__ */ new Map();
1751
+ for (const closing of structuralCadences(phrases)) {
1752
+ const type = closing.cadence.type;
1753
+ if (type !== null) {
1754
+ cadences.set(closing.phraseIndex, {
1755
+ atBeat: closing.atBeat,
1756
+ type,
1757
+ weight: closing.weight
1758
+ });
2229
1759
  }
2230
- const previous = segments[index - 1];
2231
- const following = segments[index + 1];
2232
- const before = previous !== void 0 && touches(previous, segment) ? previous : void 0;
2233
- const after = following !== void 0 && touches(segment, following) ? following : void 0;
2234
- let reason = reading({ segment, before, after, keyAt, salienceFloor: _nullishCoalesce(floors[index], () => ( 0)) });
2235
- if (reason === null && index === 0) {
2236
- reason = { kind: "first" };
2237
- }
2238
- if (reason === null && index === segments.length - 1) {
2239
- reason = { kind: "last" };
2240
- }
2241
- reason ??= _nullishCoalesce(figureReason(segment, before, after), () => ( { kind: "unfigured" }));
2242
- result.push({
2243
- chord: segment.chord,
2244
- startBeat: segment.startBeat,
2245
- endBeat: segment.endBeat,
2246
- level: levelOf(reason),
2247
- rationale: describe(reason)
2248
- });
2249
1760
  }
2250
- return result;
1761
+ const sections = sectionsFromNotes(notes, {
1762
+ meters,
1763
+ unitBars: grouping.groupBars,
1764
+ totalBeats: spanEnd,
1765
+ budget
1766
+ });
1767
+ const keys = readings.keys.length === 0 && timeline.segments.length > 0 ? [
1768
+ {
1769
+ startBeat: span.startBeat,
1770
+ endBeat: spanEnd,
1771
+ key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, prevailingKey),
1772
+ confidence: 0
1773
+ }
1774
+ ] : readings.keys.map((region) => ({
1775
+ startBeat: region.startBeat,
1776
+ endBeat: region.endBeat,
1777
+ key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, region.key),
1778
+ confidence: region.confidence
1779
+ }));
1780
+ const chords = _chunkIFZDWIKMcjs.reduceProgression.call(void 0, timeline, scaleAt).map((reduced) => {
1781
+ const index = keyIndexAt(keys, (reduced.startBeat + reduced.endBeat) / 2, prevailingKey);
1782
+ const key = keys[index].key;
1783
+ return {
1784
+ startBeat: reduced.startBeat,
1785
+ endBeat: reduced.endBeat,
1786
+ key: index,
1787
+ roman: _chunkIFZDWIKMcjs.chordToRoman.call(void 0, reduced.chord, key),
1788
+ function: _chunkIFZDWIKMcjs.functionOf.call(void 0, reduced.chord, key),
1789
+ level: reduced.level
1790
+ };
1791
+ });
1792
+ const chordOnsets = timeline.segments.map((segment) => ({
1793
+ pitch: 60,
1794
+ startBeat: segment.startBeat,
1795
+ durationBeat: segment.endBeat - segment.startBeat
1796
+ }));
1797
+ const harmonyRhythm = _chunkIFZDWIKMcjs.analyzeRhythm.call(void 0, chordOnsets, { meters, totalBeats: spanEnd, budget });
1798
+ const motifs = _chunkIFZDWIKMcjs.extractMotifs.call(void 0, line, { budget });
1799
+ const graph = _chunkIFZDWIKMcjs.motifGraph.call(void 0, line, motifs, { key: prevailingKey, budget });
1800
+ let register = null;
1801
+ if (line.length > 0) {
1802
+ const pitches = line.map((note) => note.pitch);
1803
+ register = {
1804
+ low: Math.min(...pitches),
1805
+ high: Math.max(...pitches),
1806
+ mean: _chunkIFZDWIKMcjs.weightedMean.call(void 0, line)
1807
+ };
1808
+ }
1809
+ const lineMean = _nullishCoalesce(_optionalChain([register, 'optionalAccess', _67 => _67.mean]), () => ( 0));
1810
+ const referenceMotifs = motifs.map((motif) => {
1811
+ const first = motif.occurrences[0];
1812
+ return {
1813
+ intervals: motif.intervals,
1814
+ rhythm: motif.rhythm,
1815
+ unitBeats: motif.notes.length < 2 ? 0 : motif.notes[1].startBeat - motif.notes[0].startBeat,
1816
+ spanBeats: first === void 0 ? 0 : first.endBeat - first.startBeat,
1817
+ occurrences: motif.occurrences.length
1818
+ };
1819
+ });
1820
+ const referencePhrases = phrases.map((phrase, index) => ({
1821
+ startBeat: phrase.startBeat,
1822
+ endBeat: phrase.endBeat,
1823
+ bars: _chunkIFZDWIKMcjs.barSpanOf.call(void 0, phrase.startBeat, phrase.endBeat, meters),
1824
+ section: sectionAt(sections, phrase.startBeat),
1825
+ confidence: phrase.confidence,
1826
+ cadence: _nullishCoalesce(cadences.get(index), () => ( null)),
1827
+ melody: _chunkIFZDWIKMcjs.phraseMelody.call(void 0, line, phrase.startBeat, phrase.endBeat, lineMean, meters, budget),
1828
+ motifNodes: graph.nodes.flatMap(
1829
+ (node, nodeIndex) => node.startBeat >= phrase.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && node.startBeat < phrase.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS ? [nodeIndex] : []
1830
+ )
1831
+ }));
1832
+ return {
1833
+ profileVersion: _chunkIFZDWIKMcjs.REFERENCE_PROFILE_VERSION,
1834
+ span: {
1835
+ startBeat: span.startBeat,
1836
+ endBeat: spanEnd,
1837
+ bars: _chunkIFZDWIKMcjs.barSpanOf.call(void 0, span.startBeat, spanEnd, meters)
1838
+ },
1839
+ meters,
1840
+ form: {
1841
+ hypermeter: { groupBars: grouping.groupBars, confidence: grouping.confidence },
1842
+ sections,
1843
+ phrases: referencePhrases
1844
+ },
1845
+ harmony: { keys, chords, rhythm: harmonyRhythm },
1846
+ melody: {
1847
+ register,
1848
+ shape: line.length < 2 ? null : _chunkIFZDWIKMcjs.melodicContour.call(void 0, line).shape,
1849
+ motifs: referenceMotifs,
1850
+ graph,
1851
+ rhythm: _chunkIFZDWIKMcjs.analyzeRhythm.call(void 0, line, { meters, totalBeats: spanEnd, budget })
1852
+ }
1853
+ };
1854
+ }
1855
+ function analyzeReference(notes, opts) {
1856
+ return referenceFromReadings(notes, readingsFor(notes, opts), opts);
2251
1857
  }
2252
1858
 
2253
1859
 
@@ -2264,9 +1870,5 @@ function reduceProgression(timeline, key, opts = {}) {
2264
1870
 
2265
1871
 
2266
1872
 
2267
-
2268
-
2269
-
2270
-
2271
- exports.trackRoleOf = trackRoleOf; exports.analyzePolyphony = analyzePolyphony; exports.analyzeArrangement = analyzeArrangement; exports.createArrangementSession = createArrangementSession; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.hypermeter = hypermeter; exports.melodicContour = melodicContour; exports.extractMotifs = extractMotifs; exports.motifFromNotes = motifFromNotes; exports.relateMotifs = relateMotifs; exports.compareMelodies = compareMelodies; exports.melodicSimilarity = melodicSimilarity; exports.phrasesFromTimeline = phrasesFromTimeline; exports.structuralCadences = structuralCadences; exports.sectionsFromNotes = sectionsFromNotes; exports.reduceProgression = reduceProgression;
2272
- //# sourceMappingURL=chunk-XVMJO3QF.cjs.map
1873
+ exports.trackRoleOf = trackRoleOf; exports.analyzePolyphony = analyzePolyphony; exports.analyzeArrangement = analyzeArrangement; exports.createArrangementSession = createArrangementSession; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.hypermeter = hypermeter; exports.phrasesFromTimeline = phrasesFromTimeline; exports.structuralCadences = structuralCadences; exports.sectionsFromNotes = sectionsFromNotes; exports.compareReferences = compareReferences; exports.referenceFromReadings = referenceFromReadings; exports.analyzeReference = analyzeReference;
1874
+ //# sourceMappingURL=chunk-YFA7X2F6.cjs.map