@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.
- package/NOTICE +63 -0
- package/README.md +3 -1
- package/README_ja.md +13 -11
- package/dist/analyze/index.cjs +7 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +14 -12
- package/dist/analyze/index.d.ts +14 -12
- package/dist/analyze/index.js +6 -6
- package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
- package/dist/chunk-2G5MCGYU.js.map +1 -0
- package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
- package/dist/chunk-3FDBMIPR.cjs.map +1 -0
- package/dist/{chunk-YFA7X2F6.cjs → chunk-3MDOEFIQ.cjs} +411 -327
- package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
- package/dist/{chunk-IPEF6NWB.js → chunk-5MWRBYAT.js} +278 -154
- package/dist/chunk-5MWRBYAT.js.map +1 -0
- package/dist/{chunk-B2PW6EHN.cjs → chunk-6TTBEON3.cjs} +1253 -889
- package/dist/chunk-6TTBEON3.cjs.map +1 -0
- package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
- package/dist/chunk-7NQ6AL3H.js.map +1 -0
- package/dist/{chunk-PWC4CRWT.cjs → chunk-E3QO3UET.cjs} +31 -23
- package/dist/chunk-E3QO3UET.cjs.map +1 -0
- package/dist/{chunk-GNVN4PPJ.js → chunk-FJFULJZE.js} +531 -213
- package/dist/chunk-FJFULJZE.js.map +1 -0
- package/dist/{chunk-CPMB7P75.js → chunk-FK3DGAIZ.js} +608 -244
- package/dist/chunk-FK3DGAIZ.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -1
- package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
- package/dist/chunk-H7FVMBUC.js.map +1 -0
- package/dist/{chunk-4R2LWV7E.cjs → chunk-HYSVSFNI.cjs} +664 -540
- package/dist/chunk-HYSVSFNI.cjs.map +1 -0
- package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
- package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
- package/dist/{chunk-IFZDWIKM.cjs → chunk-LQZQDBCD.cjs} +1048 -730
- package/dist/chunk-LQZQDBCD.cjs.map +1 -0
- package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
- package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
- package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
- package/dist/chunk-S3QELJUE.cjs.map +1 -0
- package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
- package/dist/chunk-TXFWSU5D.js.map +1 -0
- package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
- package/dist/chunk-U4EQTDGN.cjs.map +1 -0
- package/dist/chunk-UGLFDG7L.cjs.map +1 -1
- package/dist/{chunk-MLBPXA4Z.js → chunk-UTE5A36R.js} +198 -114
- package/dist/chunk-UTE5A36R.js.map +1 -0
- package/dist/{chunk-ZBUROTJQ.js → chunk-UZKQISRF.js} +17 -9
- package/dist/chunk-UZKQISRF.js.map +1 -0
- package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
- package/dist/chunk-VL5NXVCN.js.map +1 -0
- package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
- package/dist/chunk-WOMCXN2W.cjs.map +1 -0
- package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
- package/dist/chunk-YDGMWH43.cjs.map +1 -0
- package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
- package/dist/chunk-YYVXRV3V.js.map +1 -0
- package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
- package/dist/chunk-ZLNSH4UL.js.map +1 -0
- package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
- package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
- package/dist/core/index.cjs +5 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +9 -8
- package/dist/core/index.d.ts +9 -8
- package/dist/core/index.js +4 -4
- package/dist/generate/index.cjs +8 -8
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +28 -18
- package/dist/generate/index.d.ts +28 -18
- package/dist/generate/index.js +7 -7
- package/dist/{index-BMweUlm5.d.cts → index-5VJgKz_Q.d.cts} +6 -2
- package/dist/{index-BHIUyjhZ.d.ts → index-BLuDesVG.d.ts} +20 -13
- package/dist/{index-B9QLziDe.d.cts → index-BV6APVa6.d.cts} +20 -13
- package/dist/{index-CL4b4gUQ.d.ts → index-Bf0Sv8a9.d.ts} +6 -2
- package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
- package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
- package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
- package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
- package/dist/{index-CY-3NVfy.d.cts → index-DVInzCDS.d.cts} +53 -29
- package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
- package/dist/{index-zaZdmnU4.d.ts → index-jgeug9EA.d.ts} +53 -29
- package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
- package/dist/index.cjs +13 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +12 -12
- package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
- package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
- package/dist/model/index.cjs +12 -12
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +63 -48
- package/dist/model/index.d.ts +63 -48
- package/dist/model/index.js +11 -11
- package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
- package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
- package/dist/{profile-DLdlIpS9.d.ts → profile-B4WqcM4H.d.ts} +25 -17
- package/dist/{profile-CQMEqdH9.d.cts → profile-BJd1XkPB.d.cts} +25 -17
- package/dist/theory/index.cjs +5 -5
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +18 -14
- package/dist/theory/index.d.ts +18 -14
- package/dist/theory/index.js +4 -4
- package/dist/{types-D-XgxGvp.d.cts → types-7mGFwvMm.d.ts} +45 -22
- package/dist/{types-Dh7C064C.d.ts → types-_ofpb2be.d.cts} +45 -22
- package/package.json +13 -13
- package/dist/chunk-35NBV2FT.cjs.map +0 -1
- package/dist/chunk-3R3QVKY5.cjs.map +0 -1
- package/dist/chunk-4R2LWV7E.cjs.map +0 -1
- package/dist/chunk-76ZBTRG2.cjs.map +0 -1
- package/dist/chunk-7KG3RTYS.js.map +0 -1
- package/dist/chunk-AGOVW7U6.js.map +0 -1
- package/dist/chunk-B2PW6EHN.cjs.map +0 -1
- package/dist/chunk-CGBADIO5.cjs.map +0 -1
- package/dist/chunk-CPMB7P75.js.map +0 -1
- package/dist/chunk-GNVN4PPJ.js.map +0 -1
- package/dist/chunk-IFZDWIKM.cjs.map +0 -1
- package/dist/chunk-IPEF6NWB.js.map +0 -1
- package/dist/chunk-M5RYMAFI.js.map +0 -1
- package/dist/chunk-MLBPXA4Z.js.map +0 -1
- package/dist/chunk-NB25P3EH.js.map +0 -1
- package/dist/chunk-NM7UAMC3.cjs.map +0 -1
- package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
- package/dist/chunk-PUKJP2Z5.js.map +0 -1
- package/dist/chunk-PWC4CRWT.cjs.map +0 -1
- package/dist/chunk-RFIBF2RR.js.map +0 -1
- package/dist/chunk-S5BKZZGM.cjs.map +0 -1
- package/dist/chunk-UMWZ4ZET.js.map +0 -1
- package/dist/chunk-YFA7X2F6.cjs.map +0 -1
- 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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
45
|
-
`instrument name must be a non-empty string; received ${
|
|
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,
|
|
50
|
-
`${profile.name} articulations must be an array; received ${
|
|
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
|
-
|
|
59
|
+
_chunkS3QELJUEcjs.assertOneOf.call(void 0, articulation, _chunkS3QELJUEcjs.ARTICULATIONS, `${profile.name} articulations[${index}]`);
|
|
55
60
|
}
|
|
56
|
-
|
|
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,
|
|
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
|
-
|
|
67
|
+
_chunkS3QELJUEcjs.assertMidiPitch.call(void 0, open, `${profile.name} tuning[${index}]`);
|
|
63
68
|
}
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
76
|
-
`${profile.name} reach must be a table of limbs by voice; received ${
|
|
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
|
-
|
|
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,
|
|
98
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${profile.name} must have at least one voice within reach`);
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
201
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "playability notes", { allowNonPositiveDuration: true });
|
|
185
202
|
if (bpm !== void 0) {
|
|
186
|
-
|
|
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
|
|
249
|
-
return
|
|
258
|
+
function isReleased(endBeat, beat) {
|
|
259
|
+
return endBeat <= beat + LEGATO_OVERLAP_BEATS + _chunkS3QELJUEcjs.BEAT_EPS;
|
|
250
260
|
}
|
|
251
|
-
function
|
|
252
|
-
const
|
|
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
|
|
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 (
|
|
262
|
-
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
289
|
-
let
|
|
290
|
-
for (const
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
315
|
-
const
|
|
316
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
327
|
-
previousIndex = index;
|
|
458
|
+
hand = shifted;
|
|
328
459
|
}
|
|
329
|
-
for (const
|
|
330
|
-
checkStretch(state, profile,
|
|
331
|
-
checkPolyphony(state, profile,
|
|
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
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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,
|
|
473
|
+
function checkShiftSpeed(state, profile, from, to) {
|
|
341
474
|
const secondsPerBeat = state.secondsPerBeat;
|
|
342
|
-
if (secondsPerBeat === 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.
|
|
351
|
-
const shift = Math.abs(to.fret - from.fret);
|
|
352
|
-
if (seconds <=
|
|
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: [
|
|
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
|
|
367
|
-
|
|
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:
|
|
379
|
-
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
|
|
417
|
-
const pitch = _optionalChain([state, 'access',
|
|
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(
|
|
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(
|
|
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',
|
|
436
|
-
const right = limbsFor(kit, _optionalChain([state, 'access',
|
|
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
|
|
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
|
|
464
|
-
if (
|
|
465
|
-
const previousNote = state.notes[
|
|
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,
|
|
601
|
+
checkStrokeSpeed(state, kit.profile, previousStroke, member, chosen);
|
|
470
602
|
}
|
|
471
|
-
memory.lastStroke.set(strokeKey,
|
|
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
|
|
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,
|
|
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:
|
|
488
|
-
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,
|
|
627
|
+
checkPolyphony(state, kit.profile, { startBeat: attack.startBeat, members: strokes });
|
|
495
628
|
}
|
|
496
629
|
}
|
|
497
630
|
}
|
|
498
|
-
function checkStrokeSpeed(state, profile,
|
|
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
|
|
504
|
-
|
|
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: [
|
|
516
|
-
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
|
|
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
|
|
541
|
-
|
|
542
|
-
|
|
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 <=
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
826
|
+
_chunkS3QELJUEcjs.assertInteger.call(void 0, requested, "algorithm version");
|
|
702
827
|
if (requested < MIN_ALGORITHM_VERSION || requested > ALGORITHM_VERSION) {
|
|
703
|
-
throw new (0,
|
|
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
|
-
|
|
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
|
-
|
|
849
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, p, 0, 1, "probability");
|
|
725
850
|
return next() < p;
|
|
726
851
|
},
|
|
727
852
|
range: (lo, hi) => {
|
|
728
|
-
|
|
729
|
-
|
|
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,
|
|
856
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
732
857
|
`range lower bound must not exceed upper bound; received ${lo} > ${hi}`
|
|
733
858
|
);
|
|
734
859
|
}
|
|
735
|
-
|
|
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
|
-
|
|
740
|
-
|
|
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,
|
|
867
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
743
868
|
`float lower bound must not exceed upper bound; received ${lo} > ${hi}`
|
|
744
869
|
);
|
|
745
870
|
}
|
|
746
|
-
|
|
747
|
-
return
|
|
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
|
-
|
|
777
|
-
|
|
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
|