@libraz/libcantus 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/NOTICE +63 -0
  2. package/README.md +3 -1
  3. package/README_ja.md +13 -11
  4. package/dist/analyze/index.cjs +7 -7
  5. package/dist/analyze/index.cjs.map +1 -1
  6. package/dist/analyze/index.d.cts +14 -12
  7. package/dist/analyze/index.d.ts +14 -12
  8. package/dist/analyze/index.js +6 -6
  9. package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
  10. package/dist/chunk-2G5MCGYU.js.map +1 -0
  11. package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
  12. package/dist/chunk-3FDBMIPR.cjs.map +1 -0
  13. package/dist/{chunk-YFA7X2F6.cjs → chunk-3MDOEFIQ.cjs} +411 -327
  14. package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
  15. package/dist/{chunk-IPEF6NWB.js → chunk-5MWRBYAT.js} +278 -154
  16. package/dist/chunk-5MWRBYAT.js.map +1 -0
  17. package/dist/{chunk-B2PW6EHN.cjs → chunk-6TTBEON3.cjs} +1253 -889
  18. package/dist/chunk-6TTBEON3.cjs.map +1 -0
  19. package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
  20. package/dist/chunk-7NQ6AL3H.js.map +1 -0
  21. package/dist/{chunk-PWC4CRWT.cjs → chunk-E3QO3UET.cjs} +31 -23
  22. package/dist/chunk-E3QO3UET.cjs.map +1 -0
  23. package/dist/{chunk-GNVN4PPJ.js → chunk-FJFULJZE.js} +531 -213
  24. package/dist/chunk-FJFULJZE.js.map +1 -0
  25. package/dist/{chunk-CPMB7P75.js → chunk-FK3DGAIZ.js} +608 -244
  26. package/dist/chunk-FK3DGAIZ.js.map +1 -0
  27. package/dist/chunk-GEDTJHAC.cjs.map +1 -1
  28. package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
  29. package/dist/chunk-H7FVMBUC.js.map +1 -0
  30. package/dist/{chunk-4R2LWV7E.cjs → chunk-HYSVSFNI.cjs} +664 -540
  31. package/dist/chunk-HYSVSFNI.cjs.map +1 -0
  32. package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
  33. package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
  34. package/dist/{chunk-IFZDWIKM.cjs → chunk-LQZQDBCD.cjs} +1048 -730
  35. package/dist/chunk-LQZQDBCD.cjs.map +1 -0
  36. package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
  37. package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
  38. package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
  39. package/dist/chunk-S3QELJUE.cjs.map +1 -0
  40. package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
  41. package/dist/chunk-TXFWSU5D.js.map +1 -0
  42. package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
  43. package/dist/chunk-U4EQTDGN.cjs.map +1 -0
  44. package/dist/chunk-UGLFDG7L.cjs.map +1 -1
  45. package/dist/{chunk-MLBPXA4Z.js → chunk-UTE5A36R.js} +198 -114
  46. package/dist/chunk-UTE5A36R.js.map +1 -0
  47. package/dist/{chunk-ZBUROTJQ.js → chunk-UZKQISRF.js} +17 -9
  48. package/dist/chunk-UZKQISRF.js.map +1 -0
  49. package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
  50. package/dist/chunk-VL5NXVCN.js.map +1 -0
  51. package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
  52. package/dist/chunk-WOMCXN2W.cjs.map +1 -0
  53. package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
  54. package/dist/chunk-YDGMWH43.cjs.map +1 -0
  55. package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
  56. package/dist/chunk-YYVXRV3V.js.map +1 -0
  57. package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
  58. package/dist/chunk-ZLNSH4UL.js.map +1 -0
  59. package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
  60. package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
  61. package/dist/core/index.cjs +5 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +9 -8
  64. package/dist/core/index.d.ts +9 -8
  65. package/dist/core/index.js +4 -4
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.cjs.map +1 -1
  68. package/dist/generate/index.d.cts +28 -18
  69. package/dist/generate/index.d.ts +28 -18
  70. package/dist/generate/index.js +7 -7
  71. package/dist/{index-BMweUlm5.d.cts → index-5VJgKz_Q.d.cts} +6 -2
  72. package/dist/{index-BHIUyjhZ.d.ts → index-BLuDesVG.d.ts} +20 -13
  73. package/dist/{index-B9QLziDe.d.cts → index-BV6APVa6.d.cts} +20 -13
  74. package/dist/{index-CL4b4gUQ.d.ts → index-Bf0Sv8a9.d.ts} +6 -2
  75. package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
  76. package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
  77. package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
  78. package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
  79. package/dist/{index-CY-3NVfy.d.cts → index-DVInzCDS.d.cts} +53 -29
  80. package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
  81. package/dist/{index-zaZdmnU4.d.ts → index-jgeug9EA.d.ts} +53 -29
  82. package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
  83. package/dist/index.cjs +13 -13
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +11 -11
  86. package/dist/index.d.ts +11 -11
  87. package/dist/index.js +12 -12
  88. package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
  89. package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
  90. package/dist/model/index.cjs +12 -12
  91. package/dist/model/index.cjs.map +1 -1
  92. package/dist/model/index.d.cts +63 -48
  93. package/dist/model/index.d.ts +63 -48
  94. package/dist/model/index.js +11 -11
  95. package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
  96. package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
  97. package/dist/{profile-DLdlIpS9.d.ts → profile-B4WqcM4H.d.ts} +25 -17
  98. package/dist/{profile-CQMEqdH9.d.cts → profile-BJd1XkPB.d.cts} +25 -17
  99. package/dist/theory/index.cjs +5 -5
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +18 -14
  102. package/dist/theory/index.d.ts +18 -14
  103. package/dist/theory/index.js +4 -4
  104. package/dist/{types-D-XgxGvp.d.cts → types-7mGFwvMm.d.ts} +45 -22
  105. package/dist/{types-Dh7C064C.d.ts → types-_ofpb2be.d.cts} +45 -22
  106. package/package.json +13 -13
  107. package/dist/chunk-35NBV2FT.cjs.map +0 -1
  108. package/dist/chunk-3R3QVKY5.cjs.map +0 -1
  109. package/dist/chunk-4R2LWV7E.cjs.map +0 -1
  110. package/dist/chunk-76ZBTRG2.cjs.map +0 -1
  111. package/dist/chunk-7KG3RTYS.js.map +0 -1
  112. package/dist/chunk-AGOVW7U6.js.map +0 -1
  113. package/dist/chunk-B2PW6EHN.cjs.map +0 -1
  114. package/dist/chunk-CGBADIO5.cjs.map +0 -1
  115. package/dist/chunk-CPMB7P75.js.map +0 -1
  116. package/dist/chunk-GNVN4PPJ.js.map +0 -1
  117. package/dist/chunk-IFZDWIKM.cjs.map +0 -1
  118. package/dist/chunk-IPEF6NWB.js.map +0 -1
  119. package/dist/chunk-M5RYMAFI.js.map +0 -1
  120. package/dist/chunk-MLBPXA4Z.js.map +0 -1
  121. package/dist/chunk-NB25P3EH.js.map +0 -1
  122. package/dist/chunk-NM7UAMC3.cjs.map +0 -1
  123. package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
  124. package/dist/chunk-PUKJP2Z5.js.map +0 -1
  125. package/dist/chunk-PWC4CRWT.cjs.map +0 -1
  126. package/dist/chunk-RFIBF2RR.js.map +0 -1
  127. package/dist/chunk-S5BKZZGM.cjs.map +0 -1
  128. package/dist/chunk-UMWZ4ZET.js.map +0 -1
  129. package/dist/chunk-YFA7X2F6.cjs.map +0 -1
  130. package/dist/chunk-ZBUROTJQ.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
+ var _chunkKMYGRFZ6cjs = require('./chunk-KMYGRFZ6.cjs');
4
5
 
5
6
 
6
7
 
@@ -10,18 +11,22 @@
10
11
 
11
12
 
12
13
 
13
- var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
14
+
15
+
16
+
17
+
18
+ var _chunkS3QELJUEcjs = require('./chunk-S3QELJUE.cjs');
14
19
 
15
20
  // src/core/instrument/profile.ts
16
21
  var LIMBS = Object.freeze(["rightHand", "leftHand", "rightFoot", "leftFoot"]);
17
22
  function toInstrumentProfile(value, name = "instrument") {
18
23
  if (typeof value !== "object" || value === null) {
19
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be an instrument profile; received ${typeof value}`);
24
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name} must be an instrument profile; received ${typeof value}`);
20
25
  }
21
26
  const data = "toJSON" in value && typeof value.toJSON === "function" ? value.toJSON() : value;
22
27
  const kind = _optionalChain([data, 'optionalAccess', _2 => _2.kind]);
23
28
  if (kind !== "stringed" && kind !== "percussion") {
24
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
29
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
25
30
  `${name} must be a stringed or percussion profile; received kind ${String(kind)}`
26
31
  );
27
32
  }
@@ -31,7 +36,7 @@ function toInstrumentProfile(value, name = "instrument") {
31
36
  function toStringedProfile(value, name = "instrument") {
32
37
  const profile = toInstrumentProfile(value, name);
33
38
  if (profile.kind !== "stringed") {
34
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
39
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
35
40
  `${name} must be a stringed instrument; ${profile.name} has no strings`
36
41
  );
37
42
  }
@@ -41,50 +46,61 @@ var MAX_FRETS = 127;
41
46
  var MAX_POLYPHONY = 128;
42
47
  function assertProfile(profile) {
43
48
  if (typeof profile.name !== "string" || profile.name === "") {
44
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
45
- `instrument name must be a non-empty string; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, profile.name)}`
49
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
50
+ `instrument name must be a non-empty string; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, profile.name)}`
46
51
  );
47
52
  }
48
53
  if (!Array.isArray(profile.articulations)) {
49
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
50
- `${profile.name} articulations must be an array; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, profile.articulations)}`
54
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
55
+ `${profile.name} articulations must be an array; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, profile.articulations)}`
51
56
  );
52
57
  }
53
58
  for (const [index, articulation] of profile.articulations.entries()) {
54
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, articulation, _chunkS5BKZZGMcjs.ARTICULATIONS, `${profile.name} articulations[${index}]`);
59
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, articulation, _chunkS3QELJUEcjs.ARTICULATIONS, `${profile.name} articulations[${index}]`);
55
60
  }
56
- _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, profile.polyphony, `${profile.name} polyphony`, MAX_POLYPHONY);
61
+ _chunkS3QELJUEcjs.assertPositiveInt.call(void 0, profile.polyphony, `${profile.name} polyphony`, MAX_POLYPHONY);
57
62
  if (profile.kind === "stringed") {
58
63
  if (!Array.isArray(profile.tuning) || profile.tuning.length === 0) {
59
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${profile.name} must have at least one string`);
64
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${profile.name} must have at least one string`);
60
65
  }
61
66
  for (const [index, open] of profile.tuning.entries()) {
62
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, open, `${profile.name} tuning[${index}]`);
67
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, open, `${profile.name} tuning[${index}]`);
63
68
  }
64
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, profile.frets, `${profile.name} frets`, 0, MAX_FRETS);
65
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, profile.maxStretch, `${profile.name} maxStretch`, 0, MAX_FRETS);
69
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, profile.frets, `${profile.name} frets`, 0, MAX_FRETS);
70
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, profile.maxStretch, `${profile.name} maxStretch`, 0, MAX_FRETS);
66
71
  return;
67
72
  }
68
73
  if (!Array.isArray(profile.limbs) || profile.limbs.length === 0) {
69
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${profile.name} must have at least one limb`);
74
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${profile.name} must have at least one limb`);
70
75
  }
71
76
  for (const [index, limb] of profile.limbs.entries()) {
72
- _chunkS5BKZZGMcjs.assertOneOf.call(void 0, limb, LIMBS, `${profile.name} limbs[${index}]`);
77
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, limb, LIMBS, `${profile.name} limbs[${index}]`);
73
78
  }
74
79
  if (typeof profile.reach !== "object" || profile.reach === null) {
75
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
76
- `${profile.name} reach must be a table of limbs by voice; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, profile.reach)}`
80
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
81
+ `${profile.name} reach must be a table of limbs by voice; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, profile.reach)}`
77
82
  );
78
83
  }
79
84
  const voices = Object.keys(profile.reach);
80
85
  for (const key of voices) {
81
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, Number(key), `${profile.name} reach pitch`);
86
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, Number(key), `${profile.name} reach pitch`);
87
+ }
88
+ for (const key of voices) {
89
+ const label = `${profile.name} reach[${key}]`;
90
+ for (const [index, limb] of _chunkS3QELJUEcjs.assertArray.call(void 0,
91
+ profile.reach[key],
92
+ label
93
+ ).entries()) {
94
+ _chunkS3QELJUEcjs.assertOneOf.call(void 0, limb, LIMBS, `${label}[${index}]`);
95
+ }
82
96
  }
83
97
  if (voices.every((key) => reachOf(profile, Number(key)).length === 0)) {
84
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${profile.name} must have at least one voice within reach`);
98
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${profile.name} must have at least one voice within reach`);
85
99
  }
86
- for (const pitch of _nullishCoalesce(profile.overdub, () => ( []))) {
87
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch, `${profile.name} overdub pitch`);
100
+ if (profile.overdub !== void 0) {
101
+ for (const pitch of _chunkS3QELJUEcjs.assertArray.call(void 0, profile.overdub, `${profile.name} overdub`)) {
102
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, pitch, `${profile.name} overdub pitch`);
103
+ }
88
104
  }
89
105
  }
90
106
  function reachOf(profile, pitch) {
@@ -120,7 +136,7 @@ function rangeIn(profile) {
120
136
  }
121
137
  function canSound(profile, pitch) {
122
138
  const resolved = toInstrumentProfile(profile, "instrument");
123
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch, "pitch");
139
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, pitch, "pitch");
124
140
  return soundsIn(resolved, pitch);
125
141
  }
126
142
  function soundsIn(profile, pitch) {
@@ -131,7 +147,7 @@ function soundsIn(profile, pitch) {
131
147
  }
132
148
  function fingeringsFor(profile, pitch) {
133
149
  const stringed = toStringedProfile(profile, "instrument");
134
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch, "pitch");
150
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, pitch, "pitch");
135
151
  return fingeringsIn(stringed, pitch);
136
152
  }
137
153
  function fingeringsIn(stringed, pitch) {
@@ -150,7 +166,7 @@ function fingeringsIn(stringed, pitch) {
150
166
  }
151
167
  function foldIntoRange(pitch, profile) {
152
168
  const resolved = toInstrumentProfile(profile, "instrument");
153
- _chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, pitch, "pitch");
169
+ _chunkS3QELJUEcjs.assertMidiPitch.call(void 0, pitch, "pitch");
154
170
  return foldIn(pitch, resolved);
155
171
  }
156
172
  function foldIn(pitch, resolved) {
@@ -180,38 +196,32 @@ var MIN_DIFFICULTY = 1;
180
196
  var MAX_DIFFICULTY = 5;
181
197
  var STRING_CROSSING_COST = 2;
182
198
  var SIMULTANEITY_BEATS = 1 / 128;
199
+ var LEGATO_OVERLAP_BEATS = 1 / 16;
183
200
  function playability(notes, profile, bpm) {
184
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "playability notes", { allowNonPositiveDuration: true });
201
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "playability notes", { allowNonPositiveDuration: true });
185
202
  if (bpm !== void 0) {
186
- _chunkS5BKZZGMcjs.assertRange.call(void 0, bpm, Number.MIN_VALUE, 1e3, "playability bpm");
203
+ _chunkS3QELJUEcjs.assertRange.call(void 0, bpm, Number.MIN_VALUE, 1e3, "playability bpm");
187
204
  }
188
205
  const instrument = toInstrumentProfile(profile, "playability profile");
189
- const order = notes.map((_, index) => index).sort((a, b) => {
190
- const left = notes[a];
191
- const right = notes[b];
192
- if (!left || !right) {
193
- return a - b;
194
- }
195
- return left.startBeat - right.startBeat || left.pitch - right.pitch || a - b;
196
- });
197
206
  const state = {
198
207
  notes,
199
- order,
208
+ order: _chunkKMYGRFZ6cjs.canonicalNoteOrder.call(void 0, notes),
200
209
  placements: notes.map((_, index) => ({ note: index })),
201
210
  issues: [],
202
211
  movement: 0,
203
212
  secondsPerBeat: bpm === void 0 ? void 0 : 60 / bpm
204
213
  };
214
+ const attacks = timelineOf(state);
205
215
  if (instrument.kind === "stringed") {
206
216
  checkExistence(state, instrument, (pitch) => soundsIn(instrument, pitch));
207
- placeOnNeck(state, instrument);
217
+ placeOnNeck(state, instrument, attacks);
208
218
  } else {
209
219
  const kit = readKit(instrument);
210
220
  checkExistence(state, instrument, (pitch) => limbsFor(kit, pitch).length > 0);
211
- placeOnKit(state, kit);
221
+ placeOnKit(state, kit, attacks);
212
222
  }
213
223
  return {
214
- difficulty: difficultyOf(state, bpm),
224
+ difficulty: difficultyOf(state, attacks, bpm),
215
225
  issues: state.issues,
216
226
  placements: state.placements
217
227
  };
@@ -245,126 +255,248 @@ function checkExistence(state, profile, sounds) {
245
255
  }
246
256
  }
247
257
  }
248
- function isSimultaneous(anchorBeat, startBeat) {
249
- return startBeat - anchorBeat <= SIMULTANEITY_BEATS + _chunkS5BKZZGMcjs.BEAT_EPS;
258
+ function isReleased(endBeat, beat) {
259
+ return endBeat <= beat + LEGATO_OVERLAP_BEATS + _chunkS3QELJUEcjs.BEAT_EPS;
250
260
  }
251
- function soundingGroups(state) {
252
- const groups = [];
261
+ function timelineOf(state) {
262
+ const sounding = new Set(_chunkKMYGRFZ6cjs.soundingIndices.call(void 0, state.notes));
263
+ const attacks = [];
253
264
  let active = [];
254
- let cursor = 0;
255
- let anchor;
265
+ let current;
256
266
  for (const index of state.order) {
257
267
  const note = state.notes[index];
258
- if (!note) {
268
+ if (!note || !sounding.has(index)) {
259
269
  continue;
260
270
  }
261
- if (anchor !== void 0 && isSimultaneous(anchor, note.startBeat)) {
262
- continue;
271
+ if (!current || note.startBeat - current.startBeat > SIMULTANEITY_BEATS + _chunkS3QELJUEcjs.BEAT_EPS) {
272
+ const at = note.startBeat;
273
+ active = active.filter((held) => !isReleased(held.endBeat, at));
274
+ current = { startBeat: at, members: [], held: [...active] };
275
+ attacks.push(current);
263
276
  }
264
- const at = note.startBeat;
265
- anchor = at;
266
- active = active.filter((member) => {
267
- const held = state.notes[member];
268
- return held !== void 0 && held.startBeat + held.durationBeat > at + _chunkS5BKZZGMcjs.BEAT_EPS;
269
- });
270
- while (cursor < state.order.length) {
271
- const candidateIndex = state.order[cursor];
272
- const candidate = candidateIndex === void 0 ? void 0 : state.notes[candidateIndex];
273
- if (!candidate || !isSimultaneous(at, candidate.startBeat)) {
274
- break;
277
+ const timed = {
278
+ index,
279
+ startBeat: note.startBeat,
280
+ endBeat: note.startBeat + note.durationBeat,
281
+ attackBeat: current.startBeat
282
+ };
283
+ current.members.push(timed);
284
+ active.push(timed);
285
+ }
286
+ for (const attack of attacks) {
287
+ attack.members.sort(
288
+ (a, b) => (_nullishCoalesce(_optionalChain([state, 'access', _3 => _3.notes, 'access', _4 => _4[a.index], 'optionalAccess', _5 => _5.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([state, 'access', _6 => _6.notes, 'access', _7 => _7[b.index], 'optionalAccess', _8 => _8.pitch]), () => ( 0))) || a.index - b.index
289
+ );
290
+ }
291
+ return attacks;
292
+ }
293
+ var MAX_JOINT_PLACEMENT_NOTES = 12;
294
+ var MAX_PLACEMENT_SEARCH_NODES = 2e4;
295
+ function fitsHand(frets, maxStretch) {
296
+ let low = Number.POSITIVE_INFINITY;
297
+ let high = Number.NEGATIVE_INFINITY;
298
+ let count = 0;
299
+ for (const fret of frets) {
300
+ if (fret > 0) {
301
+ low = Math.min(low, fret);
302
+ high = Math.max(high, fret);
303
+ count += 1;
304
+ }
305
+ }
306
+ return count < 2 || high - low <= maxStretch;
307
+ }
308
+ function placeGreedily(members, previous, freeAt, heldBy) {
309
+ const free = [...freeAt];
310
+ const held = [...heldBy];
311
+ const picks = [];
312
+ const conflicts = [];
313
+ let from = previous;
314
+ for (const member of members) {
315
+ const open = member.candidates.filter(
316
+ (candidate) => isReleased(_nullishCoalesce(free[candidate.string], () => ( Number.NEGATIVE_INFINITY)), member.attackBeat)
317
+ );
318
+ if (open.length === 0) {
319
+ conflicts.push({
320
+ member,
321
+ blocked: member.candidates.map((candidate) => held[candidate.string]).filter((value) => value !== void 0)
322
+ });
323
+ }
324
+ const pool = open.length > 0 ? open : member.candidates;
325
+ const origin = from;
326
+ const chosen = pool.reduce(
327
+ (best, candidate) => fingeringCost(candidate, origin) < fingeringCost(best, origin) ? candidate : best
328
+ );
329
+ picks.push(chosen);
330
+ free[chosen.string] = Number.POSITIVE_INFINITY;
331
+ held[chosen.string] = member.index;
332
+ from = movedTo(from, chosen);
333
+ }
334
+ return { picks, conflicts };
335
+ }
336
+ function searchPlacement(members, previous, freeAt, heldFrets, maxStretch) {
337
+ const free = [...freeAt];
338
+ const frets = heldFrets === void 0 ? void 0 : [...heldFrets];
339
+ const picks = [];
340
+ let best;
341
+ let bestCost = Number.POSITIVE_INFINITY;
342
+ let nodes = 0;
343
+ const visit = (depth, from, cost) => {
344
+ if (cost >= bestCost || nodes >= MAX_PLACEMENT_SEARCH_NODES) {
345
+ return;
346
+ }
347
+ const member = members[depth];
348
+ if (member === void 0) {
349
+ best = [...picks];
350
+ bestCost = cost;
351
+ return;
352
+ }
353
+ for (const candidate of member.candidates) {
354
+ const previousFree = _nullishCoalesce(free[candidate.string], () => ( Number.NEGATIVE_INFINITY));
355
+ if (!isReleased(previousFree, member.attackBeat)) {
356
+ continue;
357
+ }
358
+ nodes += 1;
359
+ if (frets !== void 0) {
360
+ frets.push(candidate.fret);
361
+ if (!fitsHand(frets, maxStretch)) {
362
+ frets.pop();
363
+ continue;
364
+ }
275
365
  }
276
- if (candidateIndex !== void 0 && candidate.startBeat + candidate.durationBeat > at + _chunkS5BKZZGMcjs.BEAT_EPS) {
277
- active.push(candidateIndex);
366
+ free[candidate.string] = Number.POSITIVE_INFINITY;
367
+ picks.push(candidate);
368
+ visit(depth + 1, movedTo(from, candidate), cost + fingeringCost(candidate, from));
369
+ picks.pop();
370
+ free[candidate.string] = previousFree;
371
+ if (frets !== void 0) {
372
+ frets.pop();
278
373
  }
279
- cursor += 1;
280
374
  }
281
- groups.push({ startBeat: at, members: [...active] });
375
+ };
376
+ visit(0, previous, 0);
377
+ return best;
378
+ }
379
+ function placeAttack(members, previous, freeAt, heldBy, heldFrets, maxStretch) {
380
+ const greedy = placeGreedily(members, previous, freeAt, heldBy);
381
+ const soundingFrets = [...heldFrets, ...greedy.picks.map((pick) => pick.fret)];
382
+ if (greedy.conflicts.length === 0 && fitsHand(soundingFrets, maxStretch)) {
383
+ return greedy;
282
384
  }
283
- return groups;
385
+ if (members.length > MAX_JOINT_PLACEMENT_NOTES) {
386
+ return greedy;
387
+ }
388
+ const fitted = searchPlacement(members, previous, freeAt, heldFrets, maxStretch);
389
+ if (fitted !== void 0) {
390
+ return { picks: fitted, conflicts: [] };
391
+ }
392
+ if (greedy.conflicts.length > 0) {
393
+ const loose = searchPlacement(members, previous, freeAt, void 0, maxStretch);
394
+ if (loose !== void 0) {
395
+ return { picks: loose, conflicts: [] };
396
+ }
397
+ }
398
+ return greedy;
284
399
  }
285
- function placeOnNeck(state, profile) {
400
+ function placeOnNeck(state, profile, attacks) {
286
401
  const stringFreeAt = profile.tuning.map(() => Number.NEGATIVE_INFINITY);
287
402
  const stringHeldBy = profile.tuning.map(() => void 0);
288
- let previous;
289
- let previousIndex;
290
- for (const index of state.order) {
291
- const note = state.notes[index];
292
- const placement = state.placements[index];
293
- if (!note || !placement) {
294
- continue;
295
- }
296
- const candidates = fingeringsIn(profile, note.pitch);
297
- if (candidates.length === 0) {
298
- continue;
299
- }
300
- const free = candidates.filter(
301
- (candidate) => (_nullishCoalesce(stringFreeAt[candidate.string], () => ( 0))) <= note.startBeat + _chunkS5BKZZGMcjs.BEAT_EPS
302
- );
303
- if (free.length === 0) {
304
- const blocked = candidates.map((candidate) => stringHeldBy[candidate.string]).filter((held) => held !== void 0);
305
- state.issues.push({
306
- type: "stringConflict",
307
- layer: 2,
308
- notes: [.../* @__PURE__ */ new Set([...blocked, index])].sort((a, b) => a - b),
309
- startBeat: note.startBeat,
310
- impossible: true,
311
- message: `MIDI ${note.pitch} needs a string already sounding on ${profile.name}`
312
- });
403
+ let origin = {};
404
+ let hand;
405
+ for (const attack of attacks) {
406
+ const members = [];
407
+ for (const timed of attack.members) {
408
+ const note = state.notes[timed.index];
409
+ const candidates = note === void 0 ? [] : fingeringsIn(profile, note.pitch);
410
+ if (candidates.length > 0) {
411
+ members.push({ ...timed, candidates });
412
+ }
313
413
  }
314
- const pool = free.length > 0 ? free : candidates;
315
- const chosen = pool.reduce(
316
- (best, candidate) => fingeringCost(candidate, previous) < fingeringCost(best, previous) ? candidate : best
414
+ const heldFrets = attack.held.map((held) => _optionalChain([state, 'access', _9 => _9.placements, 'access', _10 => _10[held.index], 'optionalAccess', _11 => _11.fingering, 'optionalAccess', _12 => _12.fret])).filter((fret) => fret !== void 0);
415
+ const { picks, conflicts } = placeAttack(
416
+ members,
417
+ origin,
418
+ stringFreeAt,
419
+ stringHeldBy,
420
+ heldFrets,
421
+ profile.maxStretch
317
422
  );
318
- placement.fingering = chosen;
319
- stringFreeAt[chosen.string] = note.startBeat + note.durationBeat;
320
- stringHeldBy[chosen.string] = index;
321
- state.movement += 1;
322
- if (previous) {
323
- state.movement += Math.abs(chosen.fret - previous.fret) + Math.abs(chosen.string - previous.string);
324
- checkShiftSpeed(state, profile, previousIndex, index, previous, chosen);
423
+ let shifted = hand;
424
+ members.forEach((member, k) => {
425
+ const chosen = picks[k];
426
+ const placement = state.placements[member.index];
427
+ if (!chosen || !placement) {
428
+ return;
429
+ }
430
+ const conflict = conflicts.find((entry) => entry.member === member);
431
+ const note = state.notes[member.index];
432
+ if (conflict && note) {
433
+ state.issues.push({
434
+ type: "stringConflict",
435
+ layer: 2,
436
+ notes: [.../* @__PURE__ */ new Set([...conflict.blocked, member.index])].sort((a, b) => a - b),
437
+ startBeat: member.startBeat,
438
+ impossible: true,
439
+ message: `MIDI ${note.pitch} needs a string already sounding on ${profile.name}`
440
+ });
441
+ }
442
+ placement.fingering = chosen;
443
+ stringFreeAt[chosen.string] = member.endBeat;
444
+ stringHeldBy[chosen.string] = member.index;
445
+ state.movement += 1;
446
+ if (origin.string !== void 0) {
447
+ state.movement += Math.abs(chosen.string - origin.string);
448
+ }
449
+ origin = movedTo(origin, chosen);
450
+ if (chosen.fret > 0) {
451
+ shifted = { note: member, fingering: chosen };
452
+ }
453
+ });
454
+ if (hand && shifted && shifted !== hand) {
455
+ state.movement += Math.abs(shifted.fingering.fret - hand.fingering.fret);
456
+ checkShiftSpeed(state, profile, hand, shifted);
325
457
  }
326
- previous = chosen;
327
- previousIndex = index;
458
+ hand = shifted;
328
459
  }
329
- for (const group of soundingGroups(state)) {
330
- checkStretch(state, profile, group);
331
- checkPolyphony(state, profile, group);
460
+ for (const attack of attacks) {
461
+ checkStretch(state, profile, attack);
462
+ checkPolyphony(state, profile, { startBeat: attack.startBeat, members: soundingAt(attack) });
332
463
  }
333
464
  }
334
- function fingeringCost(candidate, previous) {
335
- if (!previous) {
336
- return candidate.fret + candidate.string / 100;
337
- }
338
- return Math.abs(candidate.fret - previous.fret) + STRING_CROSSING_COST * Math.abs(candidate.string - previous.string) + candidate.string / 100;
465
+ function movedTo(origin, chosen) {
466
+ return { string: chosen.string, fret: chosen.fret > 0 ? chosen.fret : origin.fret };
467
+ }
468
+ function fingeringCost(candidate, origin) {
469
+ const stopped = candidate.fret === 0 ? 0 : origin.fret === void 0 ? candidate.fret : Math.abs(candidate.fret - origin.fret);
470
+ const crossing = origin.string === void 0 ? 0 : STRING_CROSSING_COST * Math.abs(candidate.string - origin.string);
471
+ return stopped + crossing + candidate.string / 100;
339
472
  }
340
- function checkShiftSpeed(state, profile, fromIndex, toIndex, from, to) {
473
+ function checkShiftSpeed(state, profile, from, to) {
341
474
  const secondsPerBeat = state.secondsPerBeat;
342
- if (secondsPerBeat === void 0 || fromIndex === void 0) {
343
- return;
344
- }
345
- const previousNote = state.notes[fromIndex];
346
- const note = state.notes[toIndex];
347
- if (!previousNote || !note) {
475
+ if (secondsPerBeat === void 0) {
348
476
  return;
349
477
  }
350
- const seconds = (note.startBeat - previousNote.startBeat) * secondsPerBeat;
351
- const shift = Math.abs(to.fret - from.fret);
352
- if (seconds <= _chunkS5BKZZGMcjs.BEAT_EPS || shift === 0) {
478
+ const seconds = (to.note.attackBeat - from.note.attackBeat) * secondsPerBeat;
479
+ const shift = Math.abs(to.fingering.fret - from.fingering.fret);
480
+ if (seconds <= _chunkS3QELJUEcjs.BEAT_EPS || shift === 0) {
353
481
  return;
354
482
  }
355
483
  if (shift / seconds > MAX_FRET_SHIFT_PER_SECOND) {
356
484
  state.issues.push({
357
485
  type: "tooFast",
358
486
  layer: 3,
359
- notes: [fromIndex, toIndex].sort((a, b) => a - b),
360
- startBeat: note.startBeat,
487
+ notes: [from.note.index, to.note.index].sort((a, b) => a - b),
488
+ startBeat: to.note.startBeat,
361
489
  impossible: false,
362
490
  message: `a ${shift}-fret shift in ${seconds.toFixed(3)}s is beyond ${profile.name}`
363
491
  });
364
492
  }
365
493
  }
366
- function checkStretch(state, profile, group) {
367
- const frets = group.members.map((member) => _optionalChain([state, 'access', _3 => _3.placements, 'access', _4 => _4[member], 'optionalAccess', _5 => _5.fingering])).filter(
494
+ function soundingAt(attack) {
495
+ return [...attack.held, ...attack.members].map((timed) => timed.index);
496
+ }
497
+ function checkStretch(state, profile, attack) {
498
+ const sounding = soundingAt(attack);
499
+ const frets = sounding.map((member) => _optionalChain([state, 'access', _13 => _13.placements, 'access', _14 => _14[member], 'optionalAccess', _15 => _15.fingering])).filter(
368
500
  (fingering) => fingering !== void 0 && fingering.fret > 0
369
501
  ).map((fingering) => fingering.fret);
370
502
  if (frets.length < 2) {
@@ -375,8 +507,8 @@ function checkStretch(state, profile, group) {
375
507
  state.issues.push({
376
508
  type: "stretchTooWide",
377
509
  layer: 2,
378
- notes: [...group.members].sort((a, b) => a - b),
379
- startBeat: group.startBeat,
510
+ notes: sounding.sort((a, b) => a - b),
511
+ startBeat: attack.startBeat,
380
512
  impossible: true,
381
513
  message: `a ${span}-fret stretch exceeds the ${profile.maxStretch} frets one hand spans on ${profile.name}`
382
514
  });
@@ -413,10 +545,10 @@ function limbsFor(kit, pitch) {
413
545
  function passesAt(state, kit, members) {
414
546
  const played = { id: "kit", members: [] };
415
547
  const overdubs = /* @__PURE__ */ new Map();
416
- for (const index of members) {
417
- const pitch = _optionalChain([state, 'access', _6 => _6.notes, 'access', _7 => _7[index], 'optionalAccess', _8 => _8.pitch]);
548
+ for (const member of members) {
549
+ const pitch = _optionalChain([state, 'access', _16 => _16.notes, 'access', _17 => _17[member.index], 'optionalAccess', _18 => _18.pitch]);
418
550
  if (pitch === void 0 || !kit.overdub.has(pitch)) {
419
- played.members.push(index);
551
+ played.members.push(member);
420
552
  continue;
421
553
  }
422
554
  let pass = overdubs.get(pitch);
@@ -424,7 +556,7 @@ function passesAt(state, kit, members) {
424
556
  pass = { id: `overdub ${pitch}`, members: [] };
425
557
  overdubs.set(pitch, pass);
426
558
  }
427
- pass.members.push(index);
559
+ pass.members.push(member);
428
560
  }
429
561
  return [played, ...overdubs.values()];
430
562
  }
@@ -432,13 +564,13 @@ function placePass(state, kit, pass, memory) {
432
564
  const taken = /* @__PURE__ */ new Set();
433
565
  let conflicted = false;
434
566
  const members = [...pass.members].sort((a, b) => {
435
- const left = limbsFor(kit, _optionalChain([state, 'access', _9 => _9.notes, 'access', _10 => _10[a], 'optionalAccess', _11 => _11.pitch]));
436
- const right = limbsFor(kit, _optionalChain([state, 'access', _12 => _12.notes, 'access', _13 => _13[b], 'optionalAccess', _14 => _14.pitch]));
437
- return left.length - right.length || a - b;
567
+ const left = limbsFor(kit, _optionalChain([state, 'access', _19 => _19.notes, 'access', _20 => _20[a.index], 'optionalAccess', _21 => _21.pitch]));
568
+ const right = limbsFor(kit, _optionalChain([state, 'access', _22 => _22.notes, 'access', _23 => _23[b.index], 'optionalAccess', _24 => _24.pitch]));
569
+ return left.length - right.length || a.index - b.index;
438
570
  });
439
- for (const index of members) {
440
- const note = state.notes[index];
441
- const placement = state.placements[index];
571
+ for (const member of members) {
572
+ const note = state.notes[member.index];
573
+ const placement = state.placements[member.index];
442
574
  if (!note || !placement) {
443
575
  continue;
444
576
  }
@@ -460,23 +592,24 @@ function placePass(state, kit, pass, memory) {
460
592
  memory.limbForVoice.set(note.pitch, chosen);
461
593
  state.movement += 1;
462
594
  const strokeKey = `${pass.id} ${chosen}`;
463
- const previousIndex = memory.lastStroke.get(strokeKey);
464
- if (previousIndex !== void 0) {
465
- const previousNote = state.notes[previousIndex];
595
+ const previousStroke = memory.lastStroke.get(strokeKey);
596
+ if (previousStroke !== void 0) {
597
+ const previousNote = state.notes[previousStroke.index];
466
598
  if (previousNote && previousNote.pitch !== note.pitch) {
467
599
  state.movement += 1;
468
600
  }
469
- checkStrokeSpeed(state, kit.profile, previousIndex, index, chosen);
601
+ checkStrokeSpeed(state, kit.profile, previousStroke, member, chosen);
470
602
  }
471
- memory.lastStroke.set(strokeKey, index);
603
+ memory.lastStroke.set(strokeKey, member);
472
604
  }
473
605
  return conflicted;
474
606
  }
475
- function placeOnKit(state, kit) {
607
+ function placeOnKit(state, kit, attacks) {
476
608
  const memory = { limbForVoice: /* @__PURE__ */ new Map(), lastStroke: /* @__PURE__ */ new Map() };
477
- for (const group of onsetGroups(state)) {
609
+ for (const attack of attacks) {
610
+ const strokes = attack.members.map((member) => member.index);
478
611
  let conflicted = false;
479
- for (const pass of passesAt(state, kit, group.members)) {
612
+ for (const pass of passesAt(state, kit, attack.members)) {
480
613
  if (!placePass(state, kit, pass, memory)) {
481
614
  continue;
482
615
  }
@@ -484,68 +617,46 @@ function placeOnKit(state, kit) {
484
617
  state.issues.push({
485
618
  type: "limbConflict",
486
619
  layer: 2,
487
- notes: [...pass.members].sort((a, b) => a - b),
488
- startBeat: group.startBeat,
620
+ notes: pass.members.map((member) => member.index).sort((a, b) => a - b),
621
+ startBeat: attack.startBeat,
489
622
  impossible: true,
490
623
  message: `${pass.members.length} simultaneous strokes cannot be shared among the limbs of ${kit.profile.name}`
491
624
  });
492
625
  }
493
626
  if (!conflicted) {
494
- checkPolyphony(state, kit.profile, group);
627
+ checkPolyphony(state, kit.profile, { startBeat: attack.startBeat, members: strokes });
495
628
  }
496
629
  }
497
630
  }
498
- function checkStrokeSpeed(state, profile, fromIndex, toIndex, limb) {
631
+ function checkStrokeSpeed(state, profile, from, to, limb) {
499
632
  const secondsPerBeat = state.secondsPerBeat;
500
633
  if (secondsPerBeat === void 0) {
501
634
  return;
502
635
  }
503
- const previousNote = state.notes[fromIndex];
504
- const note = state.notes[toIndex];
505
- if (!previousNote || !note) {
506
- return;
507
- }
508
- const seconds = (note.startBeat - previousNote.startBeat) * secondsPerBeat;
509
- if (seconds <= _chunkS5BKZZGMcjs.BEAT_EPS || seconds >= MIN_LIMB_STROKE_SECONDS) {
636
+ const seconds = (to.attackBeat - from.attackBeat) * secondsPerBeat;
637
+ if (seconds <= _chunkS3QELJUEcjs.BEAT_EPS || seconds >= MIN_LIMB_STROKE_SECONDS) {
510
638
  return;
511
639
  }
512
640
  state.issues.push({
513
641
  type: "tooFast",
514
642
  layer: 3,
515
- notes: [fromIndex, toIndex].sort((a, b) => a - b),
516
- startBeat: note.startBeat,
643
+ notes: [from.index, to.index].sort((a, b) => a - b),
644
+ startBeat: to.startBeat,
517
645
  impossible: false,
518
646
  message: `${limb} strikes twice ${seconds.toFixed(3)}s apart on ${profile.name}`
519
647
  });
520
648
  }
521
- function onsetGroups(state) {
522
- const groups = [];
523
- let current;
524
- for (const index of state.order) {
525
- const note = state.notes[index];
526
- if (!note || note.durationBeat <= 0) {
527
- continue;
528
- }
529
- if (!current || !isSimultaneous(current.startBeat, note.startBeat)) {
530
- current = { startBeat: note.startBeat, members: [] };
531
- groups.push(current);
532
- }
533
- current.members.push(index);
534
- }
535
- return groups;
536
- }
537
- function difficultyOf(state, bpm) {
649
+ function difficultyOf(state, attacks, bpm) {
538
650
  let first = Number.POSITIVE_INFINITY;
539
651
  let last = Number.NEGATIVE_INFINITY;
540
- for (const note of state.notes) {
541
- if (!note) {
542
- continue;
652
+ for (const attack of attacks) {
653
+ for (const member of attack.members) {
654
+ first = Math.min(first, member.startBeat);
655
+ last = Math.max(last, member.endBeat);
543
656
  }
544
- first = Math.min(first, note.startBeat);
545
- last = Math.max(last, note.startBeat + Math.max(0, note.durationBeat));
546
657
  }
547
658
  const spanBeats = last - first;
548
- if (!Number.isFinite(spanBeats) || spanBeats <= _chunkS5BKZZGMcjs.BEAT_EPS) {
659
+ if (!Number.isFinite(spanBeats) || spanBeats <= _chunkS3QELJUEcjs.BEAT_EPS) {
549
660
  return MIN_DIFFICULTY;
550
661
  }
551
662
  const seconds = spanBeats * 60 / (_nullishCoalesce(bpm, () => ( REFERENCE_BPM)));
@@ -642,7 +753,7 @@ function absorbSegment(hash, segment, index) {
642
753
  return next;
643
754
  }
644
755
  if (typeof segment === "number") {
645
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, segment, `seed path segment ${index}`);
756
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, segment, `seed path segment ${index}`);
646
757
  numberBytes.setFloat64(0, segment === 0 ? 0 : segment, true);
647
758
  let next = absorb(hash, TAG_NUMBER);
648
759
  for (let i = 0; i < 8; i += 1) {
@@ -650,7 +761,7 @@ function absorbSegment(hash, segment, index) {
650
761
  }
651
762
  return next;
652
763
  }
653
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
764
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
654
765
  `seed path segment ${index} must be a string or a number; received ${typeof segment}`
655
766
  );
656
767
  }
@@ -660,7 +771,7 @@ function hashPath(domain, root, path) {
660
771
  for (let index = 0; index < path.length; index += 1) {
661
772
  const segment = path[index];
662
773
  if (segment === void 0) {
663
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
774
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
664
775
  `seed path segment ${index} must be a string or a number; received undefined`
665
776
  );
666
777
  }
@@ -668,26 +779,40 @@ function hashPath(domain, root, path) {
668
779
  }
669
780
  return fmix32(hash);
670
781
  }
782
+ var stepBytes = new DataView(new ArrayBuffer(8));
783
+ function scaleUnitDraw(unit, lo, hi) {
784
+ const value = lo + unit * (hi - lo);
785
+ if (value < hi || lo === hi) {
786
+ return value;
787
+ }
788
+ if (hi === 0) {
789
+ return -Number.MIN_VALUE;
790
+ }
791
+ stepBytes.setFloat64(0, hi);
792
+ const bits = stepBytes.getBigUint64(0);
793
+ stepBytes.setBigUint64(0, hi > 0 ? bits - 1n : bits + 1n);
794
+ return stepBytes.getFloat64(0);
795
+ }
671
796
 
672
797
  // src/core/random/positional.ts
673
798
  function createPositionalRng(seed) {
674
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, seed, "seed", 0, 4294967295);
799
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, seed, "seed", 0, 4294967295);
675
800
  return {
676
801
  at: (...path) => hashPath(DOMAIN_POSITION, seed, path) / UINT32_RANGE
677
802
  };
678
803
  }
679
804
  function includeAt(rng, complexity, ...path) {
680
- _chunkS5BKZZGMcjs.assertRange.call(void 0, complexity, 0, 1, "complexity");
805
+ _chunkS3QELJUEcjs.assertRange.call(void 0, complexity, 0, 1, "complexity");
681
806
  const draw = rng.at(...path);
682
807
  if (!(draw >= 0 && draw < 1)) {
683
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`positional draw must be in [0, 1); received ${draw}`);
808
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`positional draw must be in [0, 1); received ${draw}`);
684
809
  }
685
810
  return draw < complexity;
686
811
  }
687
812
 
688
813
  // src/core/random/seed.ts
689
814
  function deriveSeed(root, ...path) {
690
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, root, "root seed", 0, 4294967295);
815
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, root, "root seed", 0, 4294967295);
691
816
  return hashPath(DOMAIN_SEED, root, path);
692
817
  }
693
818
 
@@ -698,9 +823,9 @@ function resolveAlgorithmVersion(requested) {
698
823
  if (requested === void 0) {
699
824
  return ALGORITHM_VERSION;
700
825
  }
701
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, requested, "algorithm version");
826
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, requested, "algorithm version");
702
827
  if (requested < MIN_ALGORITHM_VERSION || requested > ALGORITHM_VERSION) {
703
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
828
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
704
829
  `algorithm version ${requested} is not produced by this build; supported versions are ${MIN_ALGORITHM_VERSION}..${ALGORITHM_VERSION}`
705
830
  );
706
831
  }
@@ -709,7 +834,7 @@ function resolveAlgorithmVersion(requested) {
709
834
 
710
835
  // src/core/random/index.ts
711
836
  function createRng(seed) {
712
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, seed, "seed", 0, 4294967295);
837
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, seed, "seed", 0, 4294967295);
713
838
  let state = seed >>> 0;
714
839
  const next = () => {
715
840
  state = state + 1831565813 >>> 0;
@@ -721,30 +846,30 @@ function createRng(seed) {
721
846
  return {
722
847
  next,
723
848
  prob: (p) => {
724
- _chunkS5BKZZGMcjs.assertRange.call(void 0, p, 0, 1, "probability");
849
+ _chunkS3QELJUEcjs.assertRange.call(void 0, p, 0, 1, "probability");
725
850
  return next() < p;
726
851
  },
727
852
  range: (lo, hi) => {
728
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, lo, "range lower bound");
729
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, hi, "range upper bound");
853
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, lo, "range lower bound");
854
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, hi, "range upper bound");
730
855
  if (lo > hi) {
731
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
856
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
732
857
  `range lower bound must not exceed upper bound; received ${lo} > ${hi}`
733
858
  );
734
859
  }
735
- _chunkS5BKZZGMcjs.assertInteger.call(void 0, hi - lo, "range span", 0);
860
+ _chunkS3QELJUEcjs.assertInteger.call(void 0, hi - lo, "range span", 0);
736
861
  return lo + Math.floor(next() * (hi - lo + 1));
737
862
  },
738
863
  float: (lo, hi) => {
739
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, lo, "float lower bound");
740
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, hi, "float upper bound");
864
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, lo, "float lower bound");
865
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, hi, "float upper bound");
741
866
  if (lo > hi) {
742
- throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
867
+ throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
743
868
  `float lower bound must not exceed upper bound; received ${lo} > ${hi}`
744
869
  );
745
870
  }
746
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, hi - lo, "float span");
747
- return lo + next() * (hi - lo);
871
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, hi - lo, "float span");
872
+ return scaleUnitDraw(next(), lo, hi);
748
873
  }
749
874
  };
750
875
  }
@@ -773,5 +898,6 @@ function createRng(seed) {
773
898
 
774
899
 
775
900
 
776
- exports.LIMBS = LIMBS; exports.toInstrumentProfile = toInstrumentProfile; exports.toStringedProfile = toStringedProfile; exports.instrumentRange = instrumentRange; exports.rangeIn = rangeIn; exports.canSound = canSound; exports.soundsIn = soundsIn; exports.fingeringsFor = fingeringsFor; exports.fingeringsIn = fingeringsIn; exports.foldIntoRange = foldIntoRange; exports.foldIn = foldIn; exports.playability = playability; exports.BASS_4_STRING = BASS_4_STRING; exports.BASS_5_STRING = BASS_5_STRING; exports.GUITAR_STANDARD = GUITAR_STANDARD; exports.GUITAR_DROP_D = GUITAR_DROP_D; exports.createPositionalRng = createPositionalRng; exports.includeAt = includeAt; exports.deriveSeed = deriveSeed; exports.ALGORITHM_VERSION = ALGORITHM_VERSION; exports.MIN_ALGORITHM_VERSION = MIN_ALGORITHM_VERSION; exports.resolveAlgorithmVersion = resolveAlgorithmVersion; exports.createRng = createRng;
777
- //# sourceMappingURL=chunk-CGBADIO5.cjs.map
901
+
902
+ exports.LIMBS = LIMBS; exports.toInstrumentProfile = toInstrumentProfile; exports.toStringedProfile = toStringedProfile; exports.instrumentRange = instrumentRange; exports.rangeIn = rangeIn; exports.canSound = canSound; exports.soundsIn = soundsIn; exports.fingeringsFor = fingeringsFor; exports.fingeringsIn = fingeringsIn; exports.foldIntoRange = foldIntoRange; exports.foldIn = foldIn; exports.playability = playability; exports.BASS_4_STRING = BASS_4_STRING; exports.BASS_5_STRING = BASS_5_STRING; exports.GUITAR_STANDARD = GUITAR_STANDARD; exports.GUITAR_DROP_D = GUITAR_DROP_D; exports.scaleUnitDraw = scaleUnitDraw; exports.createPositionalRng = createPositionalRng; exports.includeAt = includeAt; exports.deriveSeed = deriveSeed; exports.ALGORITHM_VERSION = ALGORITHM_VERSION; exports.MIN_ALGORITHM_VERSION = MIN_ALGORITHM_VERSION; exports.resolveAlgorithmVersion = resolveAlgorithmVersion; exports.createRng = createRng;
903
+ //# sourceMappingURL=chunk-WOMCXN2W.cjs.map