@libraz/libcantus 0.9.3 → 0.9.4
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 +2 -2
- package/dist/analyze/index.cjs +39 -1947
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +197 -37
- package/dist/analyze/index.d.ts +197 -37
- package/dist/analyze/index.js +19 -8
- package/dist/chunk-36N73KZ3.cjs +246 -0
- package/dist/chunk-36N73KZ3.cjs.map +1 -0
- package/dist/chunk-4HLLZPXT.cjs +3077 -0
- package/dist/chunk-4HLLZPXT.cjs.map +1 -0
- package/dist/chunk-7MGZ7D62.cjs +498 -0
- package/dist/chunk-7MGZ7D62.cjs.map +1 -0
- package/dist/{chunk-4MHA432G.js → chunk-7NKDPF74.js} +403 -180
- package/dist/chunk-7NKDPF74.js.map +1 -0
- package/dist/chunk-CAN76NCU.cjs +679 -0
- package/dist/chunk-CAN76NCU.cjs.map +1 -0
- package/dist/{chunk-MIWO425M.js → chunk-D2FAJBYL.js} +101 -34
- package/dist/chunk-D2FAJBYL.js.map +1 -0
- package/dist/{chunk-SGU7SPLK.js → chunk-EHVEBWM5.js} +1056 -531
- package/dist/chunk-EHVEBWM5.js.map +1 -0
- package/dist/chunk-FCANXBAT.cjs +30 -0
- package/dist/chunk-FCANXBAT.cjs.map +1 -0
- package/dist/chunk-FNDWXRRI.cjs +479 -0
- package/dist/chunk-FNDWXRRI.cjs.map +1 -0
- package/dist/{chunk-IT2O4XFE.js → chunk-GQSGS2N4.js} +43 -24
- package/dist/chunk-GQSGS2N4.js.map +1 -0
- package/dist/{chunk-62QZKCJQ.js → chunk-HKGYCQ34.js} +10 -6
- package/dist/chunk-HKGYCQ34.js.map +1 -0
- package/dist/{chunk-AKLYPWIN.js → chunk-I24MV65U.js} +29 -6
- package/dist/chunk-I24MV65U.js.map +1 -0
- package/dist/{chunk-CHMXELDS.js → chunk-KMHOICIY.js} +311 -181
- package/dist/chunk-KMHOICIY.js.map +1 -0
- package/dist/chunk-LW53AHQU.cjs +1117 -0
- package/dist/chunk-LW53AHQU.cjs.map +1 -0
- package/dist/{chunk-2ZWMSJH6.js → chunk-NKIBQ6PT.js} +2 -2
- package/dist/chunk-NKIBQ6PT.js.map +1 -0
- package/dist/chunk-NP4NA2FB.cjs +106 -0
- package/dist/chunk-NP4NA2FB.cjs.map +1 -0
- package/dist/chunk-NVE34YGZ.cjs +1449 -0
- package/dist/chunk-NVE34YGZ.cjs.map +1 -0
- package/dist/{chunk-EH6EM37U.js → chunk-NYYWMV2C.js} +61 -49
- package/dist/chunk-NYYWMV2C.js.map +1 -0
- package/dist/{chunk-GOAW4VYL.js → chunk-Q2XUP227.js} +243 -117
- package/dist/chunk-Q2XUP227.js.map +1 -0
- package/dist/chunk-Q6OP4U43.cjs +327 -0
- package/dist/chunk-Q6OP4U43.cjs.map +1 -0
- package/dist/chunk-RC6GBMCD.cjs +845 -0
- package/dist/chunk-RC6GBMCD.cjs.map +1 -0
- package/dist/{chunk-C5XYKUVG.js → chunk-RK35RIN7.js} +370 -157
- package/dist/chunk-RK35RIN7.js.map +1 -0
- package/dist/chunk-RUMU3UA6.cjs +53 -0
- package/dist/chunk-RUMU3UA6.cjs.map +1 -0
- package/dist/chunk-T64C7ZD7.cjs +1 -0
- package/dist/chunk-T64C7ZD7.cjs.map +1 -0
- package/dist/chunk-XEBG3G53.js +498 -0
- package/dist/chunk-XEBG3G53.js.map +1 -0
- package/dist/{chunk-I5R7ZBJS.js → chunk-YLEMGEFH.js} +103 -59
- package/dist/chunk-YLEMGEFH.js.map +1 -0
- package/dist/chunk-ZEJR4TKL.cjs +173 -0
- package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
- package/dist/core/index.cjs +130 -647
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +301 -39
- package/dist/core/index.d.ts +301 -39
- package/dist/core/index.js +54 -16
- package/dist/{function-DYt4iKsn.d.cts → function-BtiZb9i9.d.ts} +28 -7
- package/dist/{function-DVH4Eusr.d.ts → function-DNugAOrt.d.cts} +28 -7
- package/dist/generate/index.cjs +28 -4085
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +370 -62
- package/dist/generate/index.d.ts +370 -62
- package/dist/generate/index.js +23 -9
- package/dist/{index-CxqBQtH1.d.cts → index-6VR8-kit.d.ts} +19 -4
- package/dist/index-70p168VM.d.ts +192 -0
- package/dist/{index-CpOsP1y9.d.cts → index-Be74mcon.d.cts} +18 -17
- package/dist/index-BtXT6UQ0.d.cts +192 -0
- package/dist/{index-DxvARIP2.d.ts → index-C5UBbQ4G.d.ts} +18 -17
- package/dist/{index-Bt4tOUD9.d.ts → index-C83mZI1P.d.ts} +93 -8
- package/dist/{index-Bg5dYMmZ.d.ts → index-D6b6na69.d.cts} +14 -11
- package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
- package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
- package/dist/index-OG65ocOr.d.cts +272 -0
- package/dist/index-OG65ocOr.d.ts +272 -0
- package/dist/{index-Jc0Ryi79.d.ts → index-tJhkmQgE.d.cts} +19 -4
- package/dist/{index-Dikst44v.d.cts → index-tSQh-xwY.d.ts} +14 -11
- package/dist/{index-BNlGP86A.d.cts → index-y_ak9_Hi.d.cts} +93 -8
- package/dist/index.cjs +419 -7383
- 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 +111 -27
- package/dist/model/index.cjs +8 -2562
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +433 -75
- package/dist/model/index.d.ts +433 -75
- package/dist/model/index.js +6 -7
- package/dist/styled-DIRkk9Uz.d.ts +506 -0
- package/dist/styled-DJbkJQfj.d.cts +506 -0
- package/dist/theory/index.cjs +172 -1822
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +151 -233
- package/dist/theory/index.d.ts +151 -233
- package/dist/theory/index.js +30 -7
- package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
- package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
- package/package.json +24 -6
- package/dist/chunk-2ZWMSJH6.js.map +0 -1
- package/dist/chunk-4MHA432G.js.map +0 -1
- package/dist/chunk-62QZKCJQ.js.map +0 -1
- package/dist/chunk-AKLYPWIN.js.map +0 -1
- package/dist/chunk-C5XYKUVG.js.map +0 -1
- package/dist/chunk-CHMXELDS.js.map +0 -1
- package/dist/chunk-DYWFNC4A.js +0 -152
- package/dist/chunk-DYWFNC4A.js.map +0 -1
- package/dist/chunk-EH6EM37U.js.map +0 -1
- package/dist/chunk-FBQK3ZQN.js +0 -131
- package/dist/chunk-FBQK3ZQN.js.map +0 -1
- package/dist/chunk-GOAW4VYL.js.map +0 -1
- package/dist/chunk-I5R7ZBJS.js.map +0 -1
- package/dist/chunk-IT2O4XFE.js.map +0 -1
- package/dist/chunk-MIWO425M.js.map +0 -1
- package/dist/chunk-SGU7SPLK.js.map +0 -1
- package/dist/index-CGMZNCx6.d.cts +0 -140
- package/dist/index-CGMZNCx6.d.ts +0 -140
- package/dist/index-CnZCo29u.d.cts +0 -108
- package/dist/index-JnF4v9bx.d.ts +0 -108
- package/dist/styled-DZ7GjShb.d.ts +0 -282
- package/dist/styled-ZTbdaVFM.d.cts +0 -282
- package/src/analyze/arrange/index.ts +0 -21
- package/src/analyze/arrange/internal.ts +0 -127
- package/src/analyze/arrange/tension.ts +0 -199
- package/src/analyze/arrange/tracks.ts +0 -312
- package/src/analyze/detect/index.ts +0 -272
- package/src/analyze/functional/borrowed.ts +0 -84
- package/src/analyze/functional/cadence.ts +0 -66
- package/src/analyze/functional/function.ts +0 -184
- package/src/analyze/functional/index.ts +0 -23
- package/src/analyze/functional/internal.ts +0 -29
- package/src/analyze/functional/roman.ts +0 -402
- package/src/analyze/index.ts +0 -49
- package/src/analyze/timeline/index.ts +0 -451
- package/src/analyze/voice/index.ts +0 -339
- package/src/core/event-index/index.ts +0 -104
- package/src/core/index.ts +0 -61
- package/src/core/interval/index.ts +0 -81
- package/src/core/meter/index.ts +0 -302
- package/src/core/pitch/index.ts +0 -311
- package/src/core/random/index.ts +0 -76
- package/src/core/tuning/index.ts +0 -186
- package/src/core/types.ts +0 -29
- package/src/core/validation/index.ts +0 -119
- package/src/generate/bass/index.ts +0 -373
- package/src/generate/countermelody/index.ts +0 -470
- package/src/generate/drums/beat.ts +0 -266
- package/src/generate/drums/euclid.ts +0 -65
- package/src/generate/drums/fills.ts +0 -356
- package/src/generate/drums/ghost.ts +0 -119
- package/src/generate/drums/hihat.ts +0 -285
- package/src/generate/drums/hit.ts +0 -31
- package/src/generate/drums/index.ts +0 -361
- package/src/generate/drums/internal.ts +0 -257
- package/src/generate/drums/kick.ts +0 -160
- package/src/generate/drums/percussion.ts +0 -121
- package/src/generate/drums/rng.ts +0 -7
- package/src/generate/drums/swing.ts +0 -71
- package/src/generate/groove/index.ts +0 -351
- package/src/generate/harmonize/index.ts +0 -451
- package/src/generate/index.ts +0 -58
- package/src/generate/motif/index.ts +0 -422
- package/src/generate/progression/index.ts +0 -362
- package/src/generate/reharmony/index.ts +0 -323
- package/src/generate/rhythm/index.ts +0 -175
- package/src/index.ts +0 -13
- package/src/model/chord.ts +0 -442
- package/src/model/index.ts +0 -15
- package/src/model/interval.ts +0 -82
- package/src/model/key.ts +0 -266
- package/src/model/note.ts +0 -176
- package/src/model/progression.ts +0 -161
- package/src/model/shared.ts +0 -12
- package/src/theory/chord/index.ts +0 -375
- package/src/theory/chordscale/index.ts +0 -374
- package/src/theory/counterpoint/index.ts +0 -230
- package/src/theory/harmony/index.ts +0 -77
- package/src/theory/index.ts +0 -112
- package/src/theory/safety/index.ts +0 -488
- package/src/theory/scale/degrees.ts +0 -123
- package/src/theory/scale/index.ts +0 -28
- package/src/theory/scale/key.ts +0 -56
- package/src/theory/scale/masks.ts +0 -153
- package/src/theory/spelling/index.ts +0 -249
- package/src/theory/symbol/index.ts +0 -268
- package/src/theory/voicing/index.ts +0 -5
- package/src/theory/voicing/internal.ts +0 -168
- package/src/theory/voicing/leading.ts +0 -112
- package/src/theory/voicing/satb.ts +0 -216
- package/src/theory/voicing/styled.ts +0 -199
- package/tsconfig.json +0 -23
|
@@ -4,33 +4,37 @@ import {
|
|
|
4
4
|
isStrongBeat,
|
|
5
5
|
metricWeight,
|
|
6
6
|
parseTimeSignature
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YLEMGEFH.js";
|
|
8
8
|
import {
|
|
9
9
|
detectCadence,
|
|
10
10
|
detectChord,
|
|
11
11
|
detectChordBest,
|
|
12
|
-
|
|
12
|
+
detectKeyFromNotes,
|
|
13
13
|
functionOf
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KMHOICIY.js";
|
|
15
15
|
import {
|
|
16
16
|
evaluateSafety
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-NYYWMV2C.js";
|
|
18
18
|
import {
|
|
19
19
|
chordPitchClasses,
|
|
20
20
|
chordToneRole,
|
|
21
21
|
createsVerticalDissonance,
|
|
22
|
+
intervalAboveRoot,
|
|
23
|
+
isChordMember,
|
|
22
24
|
isLeadingToneResolution,
|
|
23
25
|
isScaleTone,
|
|
24
26
|
majorKey,
|
|
25
27
|
makeChord
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-D2FAJBYL.js";
|
|
27
29
|
import {
|
|
30
|
+
InvalidInputError,
|
|
28
31
|
assertFiniteNumber,
|
|
29
32
|
assertGenerationBudget,
|
|
30
33
|
assertNoteEvents,
|
|
31
34
|
assertRange,
|
|
32
|
-
assertTimeSignature
|
|
33
|
-
|
|
35
|
+
assertTimeSignature,
|
|
36
|
+
pitchClassOf
|
|
37
|
+
} from "./chunk-XEBG3G53.js";
|
|
34
38
|
|
|
35
39
|
// src/analyze/timeline/index.ts
|
|
36
40
|
function chordTimelineFromChords(chords, totalBeats) {
|
|
@@ -60,12 +64,18 @@ function chordTimelineFromChords(chords, totalBeats) {
|
|
|
60
64
|
function segmentLookup(segments) {
|
|
61
65
|
return (beat) => {
|
|
62
66
|
assertFiniteNumber(beat, "timeline query beat");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
let low = 0;
|
|
68
|
+
let high = segments.length;
|
|
69
|
+
while (low < high) {
|
|
70
|
+
const middle = Math.floor((low + high) / 2);
|
|
71
|
+
if ((segments[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat) {
|
|
72
|
+
low = middle + 1;
|
|
73
|
+
} else {
|
|
74
|
+
high = middle;
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
|
-
|
|
77
|
+
const candidate = segments[low - 1];
|
|
78
|
+
return candidate !== void 0 && beat >= candidate.startBeat && beat < candidate.endBeat ? candidate.chord : null;
|
|
69
79
|
};
|
|
70
80
|
}
|
|
71
81
|
var EPS = 1e-9;
|
|
@@ -76,9 +86,6 @@ var INEXACT_CONFIDENCE_FACTOR = 0.85;
|
|
|
76
86
|
var DIATONIC_BONUS = 0.5;
|
|
77
87
|
var EXACT_BONUS = 0.5;
|
|
78
88
|
var MISMATCH_PENALTY = 0.3;
|
|
79
|
-
function pitchClass(value) {
|
|
80
|
-
return (Math.trunc(value) % 12 + 12) % 12;
|
|
81
|
-
}
|
|
82
89
|
function scoreMatch(match, weights, maxWeight, key) {
|
|
83
90
|
const rootWeight = maxWeight > 0 ? (weights[match.rootPc] ?? 0) / maxWeight : 0;
|
|
84
91
|
const tones = chordPitchClasses(makeChord(match.rootPc, match.quality));
|
|
@@ -116,7 +123,7 @@ function analyzeWindow(notes, windowStart, windowEnd, ts, key) {
|
|
|
116
123
|
const velocityFactor = note.velocity !== void 0 ? note.velocity / 127 : 1;
|
|
117
124
|
const onsetInWindow = note.startBeat >= windowStart - EPS && note.startBeat < windowEnd - EPS;
|
|
118
125
|
const accent = onsetInWindow ? 1 + metricWeight(note.startBeat, ts) / 3 : 1;
|
|
119
|
-
const pc =
|
|
126
|
+
const pc = pitchClassOf(note.pitch);
|
|
120
127
|
weights[pc] = (weights[pc] ?? 0) + overlap * velocityFactor * accent;
|
|
121
128
|
if (note.pitch < lowestPitch) {
|
|
122
129
|
lowestPitch = note.pitch;
|
|
@@ -133,7 +140,7 @@ function analyzeWindow(notes, windowStart, windowEnd, ts, key) {
|
|
|
133
140
|
}
|
|
134
141
|
const ranked = weights.map((weight, pc) => ({ pc, weight })).filter(({ weight }) => weight > 0).sort((a, b) => b.weight - a.weight || a.pc - b.pc);
|
|
135
142
|
const selected = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
|
|
136
|
-
const bassPc =
|
|
143
|
+
const bassPc = pitchClassOf(lowestPitch);
|
|
137
144
|
const anchorPc = selected.includes(bassPc) ? bassPc : selected[0] ?? bassPc;
|
|
138
145
|
const detectionPitches = selected.map((pc) => pc === anchorPc ? pc : pc + 12);
|
|
139
146
|
const matches = detectChord(detectionPitches);
|
|
@@ -164,24 +171,28 @@ function chordTimelineFromNotes(notes, opts = {}) {
|
|
|
164
171
|
assertTimeSignature(ts);
|
|
165
172
|
const harmonicRhythm = opts.harmonicRhythm ?? beatsPerBar(ts);
|
|
166
173
|
assertRange(harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
const budget = opts.budget;
|
|
175
|
+
assertNoteEvents(notes, "timeline notes", { allowNonPositiveDuration: true, budget });
|
|
176
|
+
const soundingIndex = createNoteEventIndex(
|
|
177
|
+
notes.filter((note) => note.durationBeat > 0),
|
|
178
|
+
{ name: "timeline notes", budget }
|
|
179
|
+
);
|
|
169
180
|
const sounding = soundingIndex.notes.map(({ note }) => note);
|
|
170
181
|
const lastNoteEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
171
182
|
const totalBeats = opts.totalBeats ?? lastNoteEnd;
|
|
172
183
|
assertRange(totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
|
|
173
|
-
const key = opts.key ??
|
|
184
|
+
const key = opts.key ?? detectKeyFromNotes(sounding)[0]?.key ?? majorKey(0);
|
|
174
185
|
const segments = [];
|
|
175
186
|
const segmentConfidence = [];
|
|
176
187
|
const windowCount = Math.max(0, Math.ceil(totalBeats / harmonicRhythm - EPS));
|
|
177
|
-
assertGenerationBudget(windowCount, "timeline windows");
|
|
188
|
+
assertGenerationBudget(windowCount, "timeline windows", budget);
|
|
178
189
|
const windowNotes = Array.from({ length: windowCount }, () => []);
|
|
179
190
|
let memberships = 0;
|
|
180
191
|
for (const indexed of soundingIndex.notes) {
|
|
181
192
|
const first = Math.max(0, Math.floor(indexed.note.startBeat / harmonicRhythm));
|
|
182
193
|
const lastExclusive = Math.min(windowCount, Math.ceil(indexed.endBeat / harmonicRhythm));
|
|
183
194
|
memberships += Math.max(0, lastExclusive - first);
|
|
184
|
-
assertGenerationBudget(memberships, "timeline note-to-window memberships");
|
|
195
|
+
assertGenerationBudget(memberships, "timeline note-to-window memberships", budget);
|
|
185
196
|
for (let window = first; window < lastExclusive; window += 1) {
|
|
186
197
|
windowNotes[window]?.push(indexed.note);
|
|
187
198
|
}
|
|
@@ -232,12 +243,23 @@ function detectCadences(timeline, key) {
|
|
|
232
243
|
|
|
233
244
|
// src/analyze/arrange/internal.ts
|
|
234
245
|
var EPS2 = 1e-9;
|
|
246
|
+
var MAX_LANE_LEAP = 24;
|
|
235
247
|
function covers(note, beat) {
|
|
236
248
|
return beat >= note.startBeat - EPS2 && beat < note.endBeat - EPS2;
|
|
237
249
|
}
|
|
238
|
-
function
|
|
250
|
+
function isPercussion(role) {
|
|
251
|
+
return role === "drums";
|
|
252
|
+
}
|
|
253
|
+
function poolNotes(tracks, only) {
|
|
239
254
|
const notes = [];
|
|
240
|
-
for (
|
|
255
|
+
for (let index = 0; index < tracks.length; index += 1) {
|
|
256
|
+
const track = tracks[index];
|
|
257
|
+
if (track === void 0 || isPercussion(track.role)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (only !== void 0 && !only.has(index)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
241
263
|
for (const note of track.notes) {
|
|
242
264
|
if (note.durationBeat > 0) {
|
|
243
265
|
notes.push(note);
|
|
@@ -249,25 +271,55 @@ function poolNotes(tracks) {
|
|
|
249
271
|
function splitIntoSubVoices(ordered) {
|
|
250
272
|
const lanes = [];
|
|
251
273
|
for (const note of ordered) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
274
|
+
let best = -1;
|
|
275
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
276
|
+
let bestEnd = Number.NEGATIVE_INFINITY;
|
|
277
|
+
for (let lane = 0; lane < lanes.length; lane += 1) {
|
|
278
|
+
const last = lanes[lane]?.[(lanes[lane]?.length ?? 0) - 1];
|
|
279
|
+
if (last === void 0) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const end = last.startBeat + last.durationBeat;
|
|
283
|
+
if (end > note.startBeat + EPS2) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const distance = Math.abs(last.pitch - note.pitch);
|
|
287
|
+
if (distance > MAX_LANE_LEAP) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (distance < bestDistance || distance === bestDistance && end > bestEnd) {
|
|
291
|
+
best = lane;
|
|
292
|
+
bestDistance = distance;
|
|
293
|
+
bestEnd = end;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (best >= 0) {
|
|
297
|
+
lanes[best]?.push(note);
|
|
258
298
|
} else {
|
|
259
299
|
lanes.push([note]);
|
|
260
300
|
}
|
|
261
301
|
}
|
|
262
302
|
return lanes;
|
|
263
303
|
}
|
|
304
|
+
function lastStartingAtOrBefore(sounding, beat) {
|
|
305
|
+
let low = 0;
|
|
306
|
+
let high = sounding.length;
|
|
307
|
+
while (low < high) {
|
|
308
|
+
const middle = Math.floor((low + high) / 2);
|
|
309
|
+
if ((sounding[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat + EPS2) {
|
|
310
|
+
low = middle + 1;
|
|
311
|
+
} else {
|
|
312
|
+
high = middle;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return low - 1;
|
|
316
|
+
}
|
|
264
317
|
function prepareTracks(tracks) {
|
|
265
318
|
let nextId = 0;
|
|
266
319
|
return tracks.map((track, index) => {
|
|
267
|
-
const ordered = track.notes.filter((note) => note.durationBeat > 0).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch).map((note) => ({ ...note, id: nextId
|
|
268
|
-
const voices = splitIntoSubVoices(ordered).map((voice) =>
|
|
269
|
-
voice,
|
|
270
|
-
sounding: voice.map((note, i) => {
|
|
320
|
+
const ordered = track.notes.map((note, originalIndex) => ({ note, originalIndex })).filter(({ note }) => note.durationBeat > 0).sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch).map(({ note, originalIndex }) => ({ ...note, id: nextId++, originalIndex }));
|
|
321
|
+
const voices = splitIntoSubVoices(ordered).map((voice) => {
|
|
322
|
+
const sounding = voice.map((note, i) => {
|
|
271
323
|
const prev = i > 0 ? voice[i - 1] : void 0;
|
|
272
324
|
const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
|
|
273
325
|
const contiguous = prevEnd !== void 0 && Math.abs(prevEnd - note.startBeat) <= EPS2;
|
|
@@ -277,11 +329,20 @@ function prepareTracks(tracks) {
|
|
|
277
329
|
startBeat: note.startBeat,
|
|
278
330
|
endBeat: note.startBeat + note.durationBeat
|
|
279
331
|
};
|
|
280
|
-
})
|
|
281
|
-
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
voice,
|
|
335
|
+
sounding,
|
|
336
|
+
at: (beat) => {
|
|
337
|
+
const candidate = sounding[lastStartingAtOrBefore(sounding, beat)];
|
|
338
|
+
return candidate !== void 0 && covers(candidate, beat) ? candidate : void 0;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
});
|
|
282
342
|
return {
|
|
283
343
|
name: track.name ?? `track ${index + 1}`,
|
|
284
344
|
role: track.role ?? "other",
|
|
345
|
+
trackIndex: index,
|
|
285
346
|
voices
|
|
286
347
|
};
|
|
287
348
|
});
|
|
@@ -297,53 +358,64 @@ var FUNCTION_WEIGHT = 0.5;
|
|
|
297
358
|
var DISSONANCE_WEIGHT = 0.35;
|
|
298
359
|
var SPAN_WEIGHT = 0.15;
|
|
299
360
|
var SPAN_SATURATION = 24;
|
|
300
|
-
function pitchClass2(pitch) {
|
|
301
|
-
return (Math.trunc(pitch) % 12 + 12) % 12;
|
|
302
|
-
}
|
|
303
361
|
function clamp01(value) {
|
|
304
362
|
return Math.min(1, Math.max(0, value));
|
|
305
363
|
}
|
|
306
364
|
function tensionCurve(tracks, opts = {}) {
|
|
307
365
|
const ts = opts.ts ?? parseTimeSignature("4/4");
|
|
308
366
|
assertTimeSignature(ts);
|
|
309
|
-
|
|
367
|
+
const budget = opts.budget;
|
|
368
|
+
assertGenerationBudget(tracks.length, "arrangement tracks", budget);
|
|
310
369
|
for (let index = 0; index < tracks.length; index += 1) {
|
|
311
370
|
assertNoteEvents(tracks[index]?.notes ?? [], `tracks[${index}].notes`, {
|
|
312
|
-
allowNonPositiveDuration: true
|
|
371
|
+
allowNonPositiveDuration: true,
|
|
372
|
+
budget
|
|
313
373
|
});
|
|
314
374
|
}
|
|
315
375
|
const profile = opts.profile ?? "pop";
|
|
316
376
|
const step = opts.step ?? 1;
|
|
317
377
|
assertRange(step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
|
|
318
|
-
const
|
|
319
|
-
const
|
|
320
|
-
const
|
|
378
|
+
const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
|
|
379
|
+
const pooled = poolNotes(tracks, harmonyTracks);
|
|
380
|
+
const totalBeats = poolNotes(tracks).reduce(
|
|
381
|
+
(end, n) => Math.max(end, n.startBeat + n.durationBeat),
|
|
382
|
+
0
|
|
383
|
+
);
|
|
384
|
+
const { timeline, key } = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
|
|
321
385
|
key: opts.key,
|
|
322
386
|
ts,
|
|
323
|
-
harmonicRhythm: opts.harmonicRhythm
|
|
324
|
-
|
|
387
|
+
harmonicRhythm: opts.harmonicRhythm,
|
|
388
|
+
budget
|
|
389
|
+
}) : {
|
|
390
|
+
timeline: opts.timeline,
|
|
391
|
+
key: opts.key ?? detectKeyFromNotes(pooled)[0]?.key ?? majorKey(0)
|
|
392
|
+
};
|
|
325
393
|
const prepared = prepareTracks(tracks);
|
|
326
394
|
const points = [];
|
|
327
395
|
const sampleCount = Math.max(0, Math.ceil(totalBeats / step - EPS2));
|
|
328
|
-
assertGenerationBudget(sampleCount, "tension samples");
|
|
396
|
+
assertGenerationBudget(sampleCount, "tension samples", budget);
|
|
397
|
+
const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
|
|
398
|
+
assertGenerationBudget(sampleCount * voiceCount, "tension sample-voice lookups", budget);
|
|
329
399
|
for (let i = 0; i < sampleCount; i += 1) {
|
|
330
400
|
const beat = i * step;
|
|
331
401
|
points.push({ beat, tension: sampleTension(prepared, timeline, key, ts, profile, beat) });
|
|
332
402
|
}
|
|
333
403
|
return points;
|
|
334
404
|
}
|
|
405
|
+
function tensionCurveFrom(tracks, analysis, opts = {}) {
|
|
406
|
+
return tensionCurve(tracks, { ...opts, timeline: analysis.timeline, key: analysis.key });
|
|
407
|
+
}
|
|
335
408
|
function allSounding(prepared, beat) {
|
|
336
409
|
const out = [];
|
|
337
410
|
for (let t = 0; t < prepared.length; t += 1) {
|
|
338
411
|
const track = prepared[t];
|
|
339
|
-
if (!track) {
|
|
412
|
+
if (!track || isPercussion(track.role)) {
|
|
340
413
|
continue;
|
|
341
414
|
}
|
|
342
415
|
for (const subVoice of track.voices) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
416
|
+
const note = subVoice.at(beat);
|
|
417
|
+
if (note !== void 0) {
|
|
418
|
+
out.push({ pitch: note.pitch, track: t });
|
|
347
419
|
}
|
|
348
420
|
}
|
|
349
421
|
}
|
|
@@ -365,18 +437,21 @@ function sampleTension(prepared, timeline, key, ts, profile, beat) {
|
|
|
365
437
|
if (!voice) {
|
|
366
438
|
continue;
|
|
367
439
|
}
|
|
368
|
-
if (chordPcs && !chordPcs.has(
|
|
440
|
+
if (chordPcs && !chordPcs.has(pitchClassOf(voice.pitch))) {
|
|
369
441
|
nonChord += 1;
|
|
370
442
|
}
|
|
371
443
|
const others = sounding.filter((_, j) => j !== i).map((s) => ({ pitch: s.pitch }));
|
|
372
|
-
const result = evaluateSafety(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
444
|
+
const result = evaluateSafety(
|
|
445
|
+
{
|
|
446
|
+
profile,
|
|
447
|
+
candidatePitch: voice.pitch,
|
|
448
|
+
chord,
|
|
449
|
+
key,
|
|
450
|
+
otherVoices: others,
|
|
451
|
+
strongBeat
|
|
452
|
+
},
|
|
453
|
+
{ suggestions: false }
|
|
454
|
+
);
|
|
380
455
|
if (result.safety === 2 /* Dissonant */) {
|
|
381
456
|
dissonant += 1;
|
|
382
457
|
}
|
|
@@ -395,13 +470,10 @@ function sampleTension(prepared, timeline, key, ts, profile, beat) {
|
|
|
395
470
|
}
|
|
396
471
|
|
|
397
472
|
// src/analyze/voice/index.ts
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return (Math.trunc(pitch) % 12 + 12) % 12;
|
|
401
|
-
}
|
|
402
|
-
function intervalAboveRoot(pitch, chord) {
|
|
403
|
-
return ((pitchClass3(pitch) - pitchClass3(chord.rootPc)) % 12 + 12) % 12;
|
|
473
|
+
function toVoiceNotes(events) {
|
|
474
|
+
return events.map((event, index) => ({ ...event, id: index, originalIndex: index }));
|
|
404
475
|
}
|
|
476
|
+
var EPS3 = 1e-9;
|
|
405
477
|
function intervalAboveBass(pitch, others, chord) {
|
|
406
478
|
let bass = Number.POSITIVE_INFINITY;
|
|
407
479
|
for (const ov of others) {
|
|
@@ -411,7 +483,7 @@ function intervalAboveBass(pitch, others, chord) {
|
|
|
411
483
|
return ((pitch - bass) % 12 + 12) % 12;
|
|
412
484
|
}
|
|
413
485
|
const ref = chord.bassPc ?? chord.rootPc;
|
|
414
|
-
return ((
|
|
486
|
+
return ((pitchClassOf(pitch) - pitchClassOf(ref)) % 12 + 12) % 12;
|
|
415
487
|
}
|
|
416
488
|
function tensionDegree(ic) {
|
|
417
489
|
if (ic === 1 || ic === 2) {
|
|
@@ -422,25 +494,27 @@ function tensionDegree(ic) {
|
|
|
422
494
|
}
|
|
423
495
|
return 13;
|
|
424
496
|
}
|
|
425
|
-
function isChordMember(pitch, chord) {
|
|
426
|
-
return chord ? chordPitchClasses(chord).includes(pitchClass3(pitch)) : false;
|
|
427
|
-
}
|
|
428
497
|
function isStep(a, b) {
|
|
429
498
|
const d = Math.abs(a - b);
|
|
430
499
|
return d === 1 || d === 2;
|
|
431
500
|
}
|
|
432
501
|
function suspensionType(ic, delta) {
|
|
433
|
-
let type;
|
|
434
502
|
if (delta > 0) {
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
|
|
503
|
+
return "sus2-3";
|
|
504
|
+
}
|
|
505
|
+
if (ic === 2) {
|
|
506
|
+
return "sus9-8";
|
|
507
|
+
}
|
|
508
|
+
if (ic === 10 || ic === 11) {
|
|
509
|
+
return "sus7-6";
|
|
510
|
+
}
|
|
511
|
+
if (ic === 5 || ic === 6) {
|
|
512
|
+
return "sus4-3";
|
|
513
|
+
}
|
|
514
|
+
if (ic === 8 || ic === 9) {
|
|
515
|
+
return "sus6-5";
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
444
518
|
}
|
|
445
519
|
function stepResolution(pitch, chord) {
|
|
446
520
|
for (let delta = 1; delta <= 2; delta += 1) {
|
|
@@ -450,7 +524,7 @@ function stepResolution(pitch, chord) {
|
|
|
450
524
|
}
|
|
451
525
|
return void 0;
|
|
452
526
|
}
|
|
453
|
-
function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat) {
|
|
527
|
+
function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat = () => []) {
|
|
454
528
|
assertNoteEvents(voice, "voice notes", { allowNonPositiveDuration: true });
|
|
455
529
|
const result = [];
|
|
456
530
|
for (let i = 0; i < voice.length; i += 1) {
|
|
@@ -488,10 +562,11 @@ function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat) {
|
|
|
488
562
|
);
|
|
489
563
|
if (prepared && verticallyDissonant && resolves) {
|
|
490
564
|
const ic = intervalAboveBass(note.pitch, others, chord);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
565
|
+
const type = suspensionType(ic, next.pitch - note.pitch);
|
|
566
|
+
if (type !== null) {
|
|
567
|
+
labels.push({ kind: "suspension", type, resolveTo: next.pitch });
|
|
568
|
+
handled = true;
|
|
569
|
+
}
|
|
495
570
|
}
|
|
496
571
|
}
|
|
497
572
|
if (!handled && chord && prev && next) {
|
|
@@ -549,7 +624,18 @@ function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat) {
|
|
|
549
624
|
if (next && isLeadingToneResolution(note.pitch, next.pitch, key)) {
|
|
550
625
|
labels.push({ kind: "leadingTone", resolveTo: next.pitch });
|
|
551
626
|
}
|
|
552
|
-
|
|
627
|
+
const analyzed = {
|
|
628
|
+
noteId: note.id ?? i,
|
|
629
|
+
pitch: note.pitch,
|
|
630
|
+
startBeat: note.startBeat,
|
|
631
|
+
durationBeat: note.durationBeat,
|
|
632
|
+
labels,
|
|
633
|
+
rationale: describe(labels)
|
|
634
|
+
};
|
|
635
|
+
if (note.originalIndex !== void 0) {
|
|
636
|
+
analyzed.originalIndex = note.originalIndex;
|
|
637
|
+
}
|
|
638
|
+
result.push(analyzed);
|
|
553
639
|
}
|
|
554
640
|
return result;
|
|
555
641
|
}
|
|
@@ -587,58 +673,86 @@ function otherVoicesSounding(prepared, excludeTrack, excludeVoice, beat) {
|
|
|
587
673
|
const out = [];
|
|
588
674
|
for (let t = 0; t < prepared.length; t += 1) {
|
|
589
675
|
const track = prepared[t];
|
|
590
|
-
if (!track) {
|
|
676
|
+
if (!track || isPercussion(track.role)) {
|
|
591
677
|
continue;
|
|
592
678
|
}
|
|
593
679
|
for (let v = 0; v < track.voices.length; v += 1) {
|
|
594
680
|
if (t === excludeTrack && v === excludeVoice) {
|
|
595
681
|
continue;
|
|
596
682
|
}
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
683
|
+
const note = track.voices[v]?.at(beat);
|
|
684
|
+
if (note === void 0) {
|
|
599
685
|
continue;
|
|
600
686
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const snap = { pitch: note.pitch };
|
|
606
|
-
const previous = Math.abs(note.startBeat - beat) <= EPS2 ? note.prevPitch : note.pitch;
|
|
607
|
-
if (previous !== void 0) {
|
|
608
|
-
snap.prevPitch = previous;
|
|
609
|
-
}
|
|
610
|
-
out.push(snap);
|
|
687
|
+
const snap = { pitch: note.pitch };
|
|
688
|
+
const previous = Math.abs(note.startBeat - beat) <= EPS2 ? note.prevPitch : note.pitch;
|
|
689
|
+
if (previous !== void 0) {
|
|
690
|
+
snap.prevPitch = previous;
|
|
611
691
|
}
|
|
692
|
+
out.push(snap);
|
|
612
693
|
}
|
|
613
694
|
}
|
|
614
695
|
return out;
|
|
615
696
|
}
|
|
697
|
+
function firstSegmentAfter(timeline, beat) {
|
|
698
|
+
let low = 0;
|
|
699
|
+
let high = timeline.segments.length;
|
|
700
|
+
while (low < high) {
|
|
701
|
+
const middle = Math.floor((low + high) / 2);
|
|
702
|
+
if ((timeline.segments[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat + EPS2) {
|
|
703
|
+
low = middle + 1;
|
|
704
|
+
} else {
|
|
705
|
+
high = middle;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return low;
|
|
709
|
+
}
|
|
616
710
|
function evaluationBeats(note, timeline) {
|
|
617
711
|
const noteEnd = note.startBeat + note.durationBeat;
|
|
618
712
|
const beats = [note.startBeat];
|
|
619
|
-
for (
|
|
620
|
-
|
|
621
|
-
|
|
713
|
+
for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {
|
|
714
|
+
const start = timeline.segments[i]?.startBeat;
|
|
715
|
+
if (start === void 0 || start >= noteEnd - EPS2) {
|
|
716
|
+
break;
|
|
622
717
|
}
|
|
718
|
+
beats.push(start);
|
|
623
719
|
}
|
|
624
720
|
return beats;
|
|
625
721
|
}
|
|
626
722
|
function analyzeArrangement(tracks, opts = {}) {
|
|
627
723
|
const ts = opts.ts ?? parseTimeSignature("4/4");
|
|
628
724
|
assertTimeSignature(ts);
|
|
629
|
-
|
|
725
|
+
const budget = opts.budget;
|
|
726
|
+
assertGenerationBudget(tracks.length, "arrangement tracks", budget);
|
|
727
|
+
let noteCount = 0;
|
|
630
728
|
for (let index = 0; index < tracks.length; index += 1) {
|
|
631
|
-
|
|
632
|
-
|
|
729
|
+
const notes = tracks[index]?.notes;
|
|
730
|
+
if (!Array.isArray(notes)) {
|
|
731
|
+
throw new InvalidInputError(
|
|
732
|
+
`tracks[${index}].notes must be an array; received ${typeof notes}`
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
assertNoteEvents(notes, `tracks[${index}].notes`, {
|
|
736
|
+
allowNonPositiveDuration: true,
|
|
737
|
+
budget
|
|
633
738
|
});
|
|
739
|
+
noteCount += notes.length;
|
|
634
740
|
}
|
|
741
|
+
assertGenerationBudget(noteCount, "arrangement notes", budget);
|
|
635
742
|
const profile = opts.profile ?? "pop";
|
|
636
|
-
const
|
|
637
|
-
const
|
|
743
|
+
const minSeverity = opts.minSeverity ?? 1 /* Warning */;
|
|
744
|
+
const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
|
|
745
|
+
const pooled = poolNotes(tracks, harmonyTracks);
|
|
746
|
+
const inferred = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
|
|
638
747
|
key: opts.key,
|
|
639
748
|
ts,
|
|
640
749
|
harmonicRhythm: opts.harmonicRhythm
|
|
641
|
-
})
|
|
750
|
+
}) : {
|
|
751
|
+
timeline: opts.timeline,
|
|
752
|
+
key: opts.key ?? detectKeyFromNotes(pooled)[0]?.key ?? majorKey(0),
|
|
753
|
+
segmentConfidence: opts.timeline.segments.map(() => 1)
|
|
754
|
+
};
|
|
755
|
+
const { timeline, key, segmentConfidence } = inferred;
|
|
642
756
|
const cadences = detectCadences(timeline, key);
|
|
643
757
|
const prepared = prepareTracks(tracks);
|
|
644
758
|
const trackAnalyses = [];
|
|
@@ -648,20 +762,24 @@ function analyzeArrangement(tracks, opts = {}) {
|
|
|
648
762
|
if (!track) {
|
|
649
763
|
continue;
|
|
650
764
|
}
|
|
765
|
+
if (isPercussion(track.role)) {
|
|
766
|
+
trackAnalyses.push({ name: track.name, role: track.role, notes: [] });
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
651
769
|
const notes = [];
|
|
652
770
|
for (let v = 0; v < track.voices.length; v += 1) {
|
|
653
771
|
const subVoice = track.voices[v];
|
|
654
772
|
if (!subVoice) {
|
|
655
773
|
continue;
|
|
656
774
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
(beat) => otherVoicesSounding(prepared, t, v, beat)
|
|
663
|
-
)
|
|
775
|
+
const analyzed = analyzeVoice(
|
|
776
|
+
subVoice.voice,
|
|
777
|
+
timeline.at,
|
|
778
|
+
key,
|
|
779
|
+
(beat) => otherVoicesSounding(prepared, t, v, beat)
|
|
664
780
|
);
|
|
781
|
+
notes.push(...analyzed.map((note) => ({ ...note, trackIndex: track.trackIndex })));
|
|
782
|
+
const labelsById = new Map(analyzed.map((note) => [note.noteId, note.labels]));
|
|
665
783
|
for (let noteIndex = 0; noteIndex < subVoice.voice.length; noteIndex += 1) {
|
|
666
784
|
const note = subVoice.voice[noteIndex];
|
|
667
785
|
const preparedNote = subVoice.sounding[noteIndex];
|
|
@@ -679,14 +797,20 @@ function analyzeArrangement(tracks, opts = {}) {
|
|
|
679
797
|
otherVoices: otherVoicesSounding(prepared, t, v, beat),
|
|
680
798
|
strongBeat: isStrongBeat(beat, ts)
|
|
681
799
|
});
|
|
682
|
-
if (result.safety !== 0 /* Safe */) {
|
|
800
|
+
if (result.safety !== 0 /* Safe */ && result.safety >= minSeverity) {
|
|
683
801
|
const conflict = {
|
|
684
802
|
beat,
|
|
685
803
|
trackName: track.name,
|
|
804
|
+
trackIndex: track.trackIndex,
|
|
805
|
+
noteId: note.id,
|
|
686
806
|
pitch: note.pitch,
|
|
807
|
+
labels: labelsById.get(note.id) ?? [],
|
|
687
808
|
safety: result.safety,
|
|
688
809
|
reasons: result.reasons
|
|
689
810
|
};
|
|
811
|
+
if (note.originalIndex !== void 0) {
|
|
812
|
+
conflict.originalIndex = note.originalIndex;
|
|
813
|
+
}
|
|
690
814
|
if (result.rationale !== void 0) {
|
|
691
815
|
conflict.rationale = result.rationale;
|
|
692
816
|
}
|
|
@@ -713,7 +837,9 @@ export {
|
|
|
713
837
|
chordTimelineFromNotes,
|
|
714
838
|
detectCadences,
|
|
715
839
|
tensionCurve,
|
|
840
|
+
tensionCurveFrom,
|
|
841
|
+
toVoiceNotes,
|
|
716
842
|
analyzeVoice,
|
|
717
843
|
analyzeArrangement
|
|
718
844
|
};
|
|
719
|
-
//# sourceMappingURL=chunk-
|
|
845
|
+
//# sourceMappingURL=chunk-Q2XUP227.js.map
|