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