@libraz/libcantus 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/README_ja.md +4 -0
- package/dist/analyze/index.cjs +31 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +155 -9
- package/dist/analyze/index.d.ts +155 -9
- package/dist/analyze/index.js +37 -13
- package/dist/{chunk-TT6PKMPR.cjs → chunk-35NBV2FT.cjs} +16 -16
- package/dist/{chunk-TT6PKMPR.cjs.map → chunk-35NBV2FT.cjs.map} +1 -1
- package/dist/{chunk-YPB3A3EY.cjs → chunk-3R3QVKY5.cjs} +314 -285
- package/dist/chunk-3R3QVKY5.cjs.map +1 -0
- package/dist/{chunk-GDKE6RQW.cjs → chunk-4R2LWV7E.cjs} +691 -413
- package/dist/chunk-4R2LWV7E.cjs.map +1 -0
- package/dist/{chunk-RVS4G4MR.cjs → chunk-76ZBTRG2.cjs} +198 -198
- package/dist/{chunk-RVS4G4MR.cjs.map → chunk-76ZBTRG2.cjs.map} +1 -1
- package/dist/{chunk-X6RJAHII.js → chunk-7KG3RTYS.js} +2 -2
- package/dist/{chunk-PQL7E5F6.js → chunk-AGOVW7U6.js} +1 -1
- package/dist/{chunk-PQL7E5F6.js.map → chunk-AGOVW7U6.js.map} +1 -1
- package/dist/{chunk-5KW7DNCJ.cjs → chunk-B2PW6EHN.cjs} +2423 -453
- package/dist/chunk-B2PW6EHN.cjs.map +1 -0
- package/dist/{chunk-J772DL3B.cjs → chunk-CGBADIO5.cjs} +53 -53
- package/dist/{chunk-J772DL3B.cjs.map → chunk-CGBADIO5.cjs.map} +1 -1
- package/dist/{chunk-R5VUXQMP.js → chunk-CPMB7P75.js} +1978 -8
- package/dist/chunk-CPMB7P75.js.map +1 -0
- package/dist/{chunk-VLE5R7PF.js → chunk-GNVN4PPJ.js} +1874 -8
- package/dist/chunk-GNVN4PPJ.js.map +1 -0
- package/dist/{chunk-Q6R3QAZD.cjs → chunk-IFZDWIKM.cjs} +2199 -333
- package/dist/chunk-IFZDWIKM.cjs.map +1 -0
- package/dist/{chunk-DKICR7AN.js → chunk-IPEF6NWB.js} +296 -18
- package/dist/chunk-IPEF6NWB.js.map +1 -0
- package/dist/{chunk-AWEMD747.js → chunk-M5RYMAFI.js} +3 -3
- package/dist/{chunk-HSY2VQXZ.js → chunk-MLBPXA4Z.js} +330 -728
- package/dist/chunk-MLBPXA4Z.js.map +1 -0
- package/dist/{chunk-H66ARFU5.js → chunk-NB25P3EH.js} +3 -3
- package/dist/{chunk-5JJ6EHEZ.cjs → chunk-NM7UAMC3.cjs} +62 -62
- package/dist/{chunk-5JJ6EHEZ.cjs.map → chunk-NM7UAMC3.cjs.map} +1 -1
- package/dist/{chunk-R7B5N2JK.cjs → chunk-PM4XJE2Y.cjs} +47 -47
- package/dist/{chunk-R7B5N2JK.cjs.map → chunk-PM4XJE2Y.cjs.map} +1 -1
- package/dist/{chunk-7GOKQJTQ.js → chunk-PUKJP2Z5.js} +2 -2
- package/dist/{chunk-QCXYKSZS.cjs → chunk-PWC4CRWT.cjs} +19 -19
- package/dist/{chunk-QCXYKSZS.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
- package/dist/{chunk-FPBZFNCD.js → chunk-RFIBF2RR.js} +47 -18
- package/dist/{chunk-FPBZFNCD.js.map → chunk-RFIBF2RR.js.map} +1 -1
- package/dist/{chunk-XKSO2V5I.cjs → chunk-S5BKZZGM.cjs} +1 -1
- package/dist/{chunk-XKSO2V5I.cjs.map → chunk-S5BKZZGM.cjs.map} +1 -1
- package/dist/{chunk-HOHFYGKW.js → chunk-UMWZ4ZET.js} +2 -2
- package/dist/{chunk-XVMJO3QF.cjs → chunk-YFA7X2F6.cjs} +489 -887
- package/dist/chunk-YFA7X2F6.cjs.map +1 -0
- package/dist/{chunk-57WB2EDF.js → chunk-ZBUROTJQ.js} +3 -3
- package/dist/{coerce-wwdELNYS.d.ts → coerce-DP0vYu-x.d.ts} +77 -1
- package/dist/{coerce-CJlgULrQ.d.cts → coerce-DRERzdve.d.cts} +77 -1
- package/dist/core/index.cjs +5 -5
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +4 -4
- package/dist/generate/index.cjs +20 -8
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +87 -9
- package/dist/generate/index.d.ts +87 -9
- package/dist/generate/index.js +19 -7
- package/dist/{index-BaoKVVhE.d.cts → index-B9QLziDe.d.cts} +2 -2
- package/dist/{index-CnHQgqNh.d.ts → index-BHIUyjhZ.d.ts} +2 -2
- package/dist/{index-CBCP84rv.d.cts → index-BL5N2Oyz.d.cts} +1 -1
- package/dist/{index-D_hzxD8w.d.cts → index-BMweUlm5.d.cts} +3 -0
- package/dist/{index-Ci3xyows.d.ts → index-CL4b4gUQ.d.ts} +3 -0
- package/dist/{index-BzLNkmA7.d.cts → index-CY-3NVfy.d.cts} +486 -5
- package/dist/{index-CnIdP4EZ.d.ts → index-Dw2DP7vm.d.ts} +1 -1
- package/dist/{index-DpC-mDlV.d.ts → index-zaZdmnU4.d.ts} +486 -5
- package/dist/index.cjs +49 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +55 -19
- package/dist/{masks-O73Rhj5d.d.ts → masks-Cc7-MzzY.d.ts} +1 -1
- package/dist/{masks-C8TWLFIi.d.cts → masks-pADfTOiD.d.cts} +1 -1
- package/dist/model/index.cjs +12 -12
- package/dist/model/index.d.cts +213 -16
- package/dist/model/index.d.ts +213 -16
- package/dist/model/index.js +11 -11
- package/dist/{index-DTsKaPaC.d.cts → profile-CQMEqdH9.d.cts} +88 -601
- package/dist/{index-BUtIf2go.d.ts → profile-DLdlIpS9.d.ts} +88 -601
- package/dist/theory/index.cjs +5 -5
- package/dist/theory/index.d.cts +7 -7
- package/dist/theory/index.d.ts +7 -7
- package/dist/theory/index.js +4 -4
- package/dist/{index-BpWOhkkE.d.ts → types-D-XgxGvp.d.cts} +1170 -14
- package/dist/{index-B3WCj_Hm.d.cts → types-Dh7C064C.d.ts} +1170 -14
- package/package.json +6 -5
- package/dist/chunk-5KW7DNCJ.cjs.map +0 -1
- package/dist/chunk-DKICR7AN.js.map +0 -1
- package/dist/chunk-GDKE6RQW.cjs.map +0 -1
- package/dist/chunk-HSY2VQXZ.js.map +0 -1
- package/dist/chunk-Q6R3QAZD.cjs.map +0 -1
- package/dist/chunk-R5VUXQMP.js.map +0 -1
- package/dist/chunk-VLE5R7PF.js.map +0 -1
- package/dist/chunk-XVMJO3QF.cjs.map +0 -1
- package/dist/chunk-YPB3A3EY.cjs.map +0 -1
- /package/dist/{chunk-X6RJAHII.js.map → chunk-7KG3RTYS.js.map} +0 -0
- /package/dist/{chunk-AWEMD747.js.map → chunk-M5RYMAFI.js.map} +0 -0
- /package/dist/{chunk-H66ARFU5.js.map → chunk-NB25P3EH.js.map} +0 -0
- /package/dist/{chunk-7GOKQJTQ.js.map → chunk-PUKJP2Z5.js.map} +0 -0
- /package/dist/{chunk-HOHFYGKW.js.map → chunk-UMWZ4ZET.js.map} +0 -0
- /package/dist/{chunk-57WB2EDF.js.map → chunk-ZBUROTJQ.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunk35NBV2FTcjs = require('./chunk-35NBV2FT.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
@@ -47,8 +47,8 @@ var _chunkTT6PKMPRcjs = require('./chunk-TT6PKMPR.cjs');
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
|
|
51
50
|
|
|
51
|
+
var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
@@ -77,18 +77,31 @@ var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
|
|
81
94
|
|
|
82
95
|
// src/analyze/detect/modes.ts
|
|
83
96
|
var MODAL_CANDIDATES = Object.freeze([
|
|
84
|
-
{ scaleName: "dorian", mode: "minor", mask:
|
|
85
|
-
{ scaleName: "phrygian", mode: "minor", mask:
|
|
86
|
-
{ scaleName: "lydian", mode: "major", mask:
|
|
87
|
-
{ scaleName: "mixolydian", mode: "major", mask:
|
|
97
|
+
{ scaleName: "dorian", mode: "minor", mask: _chunk3R3QVKY5cjs.DORIAN_MASK, substitutions: [[8, 9]] },
|
|
98
|
+
{ scaleName: "phrygian", mode: "minor", mask: _chunk3R3QVKY5cjs.PHRYGIAN_MASK, substitutions: [[2, 1]] },
|
|
99
|
+
{ scaleName: "lydian", mode: "major", mask: _chunk3R3QVKY5cjs.LYDIAN_MASK, substitutions: [[5, 6]] },
|
|
100
|
+
{ scaleName: "mixolydian", mode: "major", mask: _chunk3R3QVKY5cjs.MIXOLYDIAN_MASK, substitutions: [[11, 10]] },
|
|
88
101
|
{
|
|
89
102
|
scaleName: "locrian",
|
|
90
103
|
mode: "minor",
|
|
91
|
-
mask:
|
|
104
|
+
mask: _chunk3R3QVKY5cjs.LOCRIAN_MASK,
|
|
92
105
|
substitutions: [
|
|
93
106
|
[2, 1],
|
|
94
107
|
[7, 6]
|
|
@@ -115,12 +128,12 @@ function resolveModalCandidates(modes) {
|
|
|
115
128
|
return MODAL_CANDIDATES;
|
|
116
129
|
}
|
|
117
130
|
if (!Array.isArray(modes)) {
|
|
118
|
-
throw new (0,
|
|
131
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("modes must be a boolean or an array of mode names");
|
|
119
132
|
}
|
|
120
133
|
const requested = new Set(modes);
|
|
121
134
|
for (const name of requested) {
|
|
122
135
|
if (!MODAL_SCALE_NAMES.some((known) => known === name)) {
|
|
123
|
-
throw new (0,
|
|
136
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
124
137
|
`unknown mode '${String(name)}'; expected one of ${MODAL_SCALE_NAMES.join(", ")}`
|
|
125
138
|
);
|
|
126
139
|
}
|
|
@@ -131,7 +144,7 @@ function resolveModalCandidates(modes) {
|
|
|
131
144
|
// src/analyze/adjacency.ts
|
|
132
145
|
var HUMANIZE_ADJACENCY = 0.05;
|
|
133
146
|
function sameInstant(beat, other) {
|
|
134
|
-
return Math.abs(beat - other) <=
|
|
147
|
+
return Math.abs(beat - other) <= _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
135
148
|
}
|
|
136
149
|
function adjacent(prevEnd, nextStart) {
|
|
137
150
|
return Math.abs(nextStart - prevEnd) <= HUMANIZE_ADJACENCY;
|
|
@@ -145,7 +158,7 @@ var MAX_VELOCITY = 127;
|
|
|
145
158
|
var ACCENT_DIVISOR = 3;
|
|
146
159
|
function noteWeight(note, beats, meter) {
|
|
147
160
|
const velocityFactor = note.velocity !== void 0 ? note.velocity / MAX_VELOCITY : 1;
|
|
148
|
-
const accent = meter === void 0 ? 1 : 1 +
|
|
161
|
+
const accent = meter === void 0 ? 1 : 1 + _chunkS5BKZZGMcjs.metricWeight.call(void 0, note.startBeat, meter) / ACCENT_DIVISOR;
|
|
149
162
|
return beats * velocityFactor * accent;
|
|
150
163
|
}
|
|
151
164
|
function bucketNotesBySlot(notes, grid, slotCount, opts) {
|
|
@@ -158,7 +171,7 @@ function bucketNotesBySlot(notes, grid, slotCount, opts) {
|
|
|
158
171
|
Math.ceil((note.startBeat + note.durationBeat - grid.origin) / grid.slotBeats)
|
|
159
172
|
);
|
|
160
173
|
memberships += Math.max(0, lastExclusive - first);
|
|
161
|
-
|
|
174
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, memberships, opts.name, opts.budget);
|
|
162
175
|
for (let slot = first; slot < lastExclusive; slot += 1) {
|
|
163
176
|
_optionalChain([buckets, 'access', _2 => _2[slot], 'optionalAccess', _3 => _3.push, 'call', _4 => _4(note)]);
|
|
164
177
|
}
|
|
@@ -171,14 +184,14 @@ function windowWeights(notes, windowStart, windowEnd, meter) {
|
|
|
171
184
|
for (const note of notes) {
|
|
172
185
|
const noteEnd = note.startBeat + note.durationBeat;
|
|
173
186
|
const overlap = Math.min(noteEnd, windowEnd) - Math.max(note.startBeat, windowStart);
|
|
174
|
-
if (overlap <=
|
|
187
|
+
if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
175
188
|
continue;
|
|
176
189
|
}
|
|
177
|
-
const onsetInWindow = note.startBeat >= windowStart -
|
|
178
|
-
const pc =
|
|
190
|
+
const onsetInWindow = note.startBeat >= windowStart - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < windowEnd - _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
191
|
+
const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, note.pitch);
|
|
179
192
|
const weight = noteWeight(note, overlap, onsetInWindow ? meter : void 0);
|
|
180
193
|
weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + weight;
|
|
181
|
-
if (weight >
|
|
194
|
+
if (weight > _chunkS5BKZZGMcjs.BEAT_EPS && note.pitch < lowestPitch) {
|
|
182
195
|
lowestPitch = note.pitch;
|
|
183
196
|
}
|
|
184
197
|
}
|
|
@@ -213,12 +226,12 @@ var NAMED_PROFILES = {
|
|
|
213
226
|
};
|
|
214
227
|
function assertProfileVector(vector, name) {
|
|
215
228
|
if (vector.length !== PROFILE_LENGTH) {
|
|
216
|
-
throw new (0,
|
|
229
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must have ${PROFILE_LENGTH} entries`);
|
|
217
230
|
}
|
|
218
231
|
for (let index = 0; index < vector.length; index += 1) {
|
|
219
232
|
const value = vector[index];
|
|
220
233
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
221
|
-
throw new (0,
|
|
234
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}[${index}] must be a finite number`);
|
|
222
235
|
}
|
|
223
236
|
}
|
|
224
237
|
}
|
|
@@ -229,18 +242,18 @@ function resolveKeyProfile(profile) {
|
|
|
229
242
|
if (typeof profile === "string") {
|
|
230
243
|
const named = NAMED_PROFILES[profile];
|
|
231
244
|
if (named === void 0) {
|
|
232
|
-
throw new (0,
|
|
245
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
233
246
|
`unknown key profile '${profile}'; expected one of ${Object.keys(NAMED_PROFILES).join(", ")}`
|
|
234
247
|
);
|
|
235
248
|
}
|
|
236
249
|
return named;
|
|
237
250
|
}
|
|
238
251
|
if (typeof profile !== "object" || profile === null) {
|
|
239
|
-
throw new (0,
|
|
252
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("profile must be a profile name or a { major, minor } pair");
|
|
240
253
|
}
|
|
241
254
|
const { major, minor } = profile;
|
|
242
255
|
if (!Array.isArray(major) || !Array.isArray(minor)) {
|
|
243
|
-
throw new (0,
|
|
256
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("profile must carry a major and a minor vector");
|
|
244
257
|
}
|
|
245
258
|
assertProfileVector(major, "profile.major");
|
|
246
259
|
assertProfileVector(minor, "profile.minor");
|
|
@@ -290,44 +303,44 @@ function profileScore(dist, vector, tonic) {
|
|
|
290
303
|
|
|
291
304
|
// src/analyze/detect/index.ts
|
|
292
305
|
function uniquePitchClasses(pitches) {
|
|
293
|
-
return [...new Set(pitches.map(
|
|
306
|
+
return [...new Set(pitches.map(_chunkS5BKZZGMcjs.pitchClassOf))].sort((a, b) => a - b);
|
|
294
307
|
}
|
|
295
308
|
function assertPitches(pitches, name, budget = void 0) {
|
|
296
|
-
|
|
297
|
-
|
|
309
|
+
_chunkS5BKZZGMcjs.assertArray.call(void 0, pitches, name);
|
|
310
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, pitches.length, name, budget);
|
|
298
311
|
for (let index = 0; index < pitches.length; index += 1) {
|
|
299
|
-
|
|
312
|
+
_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, _nullishCoalesce(pitches[index], () => ( Number.NaN)), `pitches[${index}]`);
|
|
300
313
|
}
|
|
301
314
|
}
|
|
302
315
|
function detectChord(pitches, opts = {}) {
|
|
303
|
-
const asked =
|
|
316
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
304
317
|
assertPitches(pitches, "chord detection pitches");
|
|
305
|
-
if (asked.bassPc !== void 0)
|
|
318
|
+
if (asked.bassPc !== void 0) _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, asked.bassPc, "bassPc");
|
|
306
319
|
const input = uniquePitchClasses(pitches);
|
|
307
320
|
if (input.length === 0) {
|
|
308
321
|
return [];
|
|
309
322
|
}
|
|
310
323
|
const inputKind = asked.input === void 0 || asked.input === "auto" ? pitches.every((pitch) => Number.isInteger(pitch) && pitch >= 0 && pitch <= 11) ? "pitchClass" : "midi" : asked.input;
|
|
311
|
-
const bassPc = asked.bassPc !== void 0 ?
|
|
324
|
+
const bassPc = asked.bassPc !== void 0 ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, asked.bassPc) : inputKind === "midi" ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
|
|
312
325
|
const inputSet = new Set(input);
|
|
313
326
|
const matches = [];
|
|
314
|
-
const qualities =
|
|
327
|
+
const qualities = _chunk3R3QVKY5cjs.chordQualities.call(void 0, );
|
|
315
328
|
const toneCounts = new Map(
|
|
316
|
-
qualities.map((quality) => [quality,
|
|
329
|
+
qualities.map((quality) => [quality, _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, 0, quality)).length])
|
|
317
330
|
);
|
|
318
331
|
const qualityRank = new Map(qualities.map((quality, index) => [quality, index]));
|
|
319
332
|
for (const rootPc of input) {
|
|
320
333
|
for (const quality of qualities) {
|
|
321
|
-
const chord =
|
|
322
|
-
const tones =
|
|
334
|
+
const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, quality);
|
|
335
|
+
const tones = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord);
|
|
323
336
|
const toneSet = new Set(tones);
|
|
324
337
|
const missingPcs = tones.filter((pc) => !inputSet.has(pc));
|
|
325
|
-
const fifthPc =
|
|
338
|
+
const fifthPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + 7);
|
|
326
339
|
if (missingPcs.length > 1 || missingPcs.length === 1 && missingPcs[0] !== fifthPc) {
|
|
327
340
|
continue;
|
|
328
341
|
}
|
|
329
342
|
const extraPcs = input.filter((pc) => !toneSet.has(pc));
|
|
330
|
-
const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) =>
|
|
343
|
+
const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + iv) === bassPc);
|
|
331
344
|
const inversion = bassPc === void 0 || bassIndex < 0 ? null : bassIndex;
|
|
332
345
|
const match = {
|
|
333
346
|
rootPc,
|
|
@@ -372,15 +385,15 @@ function detectChordBest(pitches, opts = {}) {
|
|
|
372
385
|
if (!best) {
|
|
373
386
|
return null;
|
|
374
387
|
}
|
|
375
|
-
return
|
|
388
|
+
return _chunk3R3QVKY5cjs.makeChord.call(void 0, best.rootPc, best.quality, best.bassPc);
|
|
376
389
|
}
|
|
377
390
|
var MINOR_VARIANTS = [
|
|
378
|
-
{ variant: "natural", mask:
|
|
379
|
-
{ variant: "harmonic", mask:
|
|
380
|
-
{ variant: "melodic", mask:
|
|
391
|
+
{ variant: "natural", mask: _chunk3R3QVKY5cjs.NATURAL_MINOR_MASK, scaleName: "naturalMinor" },
|
|
392
|
+
{ variant: "harmonic", mask: _chunk3R3QVKY5cjs.HARMONIC_MINOR_MASK, scaleName: "harmonicMinor" },
|
|
393
|
+
{ variant: "melodic", mask: _chunk3R3QVKY5cjs.MELODIC_MINOR_MASK, scaleName: "melodicMinor" }
|
|
381
394
|
];
|
|
382
395
|
var MAJOR_VARIANTS = [
|
|
383
|
-
{ variant: "major", mask:
|
|
396
|
+
{ variant: "major", mask: _chunk3R3QVKY5cjs.MAJOR_MASK, scaleName: "major" }
|
|
384
397
|
];
|
|
385
398
|
var SCALE_NAME_ORDER = [
|
|
386
399
|
"major",
|
|
@@ -407,7 +420,7 @@ function quoteScores(rival, winner) {
|
|
|
407
420
|
return `${rival.toFixed(digits)} against ${winner.toFixed(digits)}`;
|
|
408
421
|
}
|
|
409
422
|
function keyLabel(match) {
|
|
410
|
-
const tonic =
|
|
423
|
+
const tonic = _chunkS5BKZZGMcjs.formatNote.call(void 0, _chunk3R3QVKY5cjs.spelledKeyOf.call(void 0, match.key).tonic);
|
|
411
424
|
const scale = match.scaleName.replace(/([A-Z])/g, " $1").toLowerCase();
|
|
412
425
|
return `${tonic} ${scale}`;
|
|
413
426
|
}
|
|
@@ -438,11 +451,11 @@ function explainMatches(results, inputSize) {
|
|
|
438
451
|
winner.alternatives = results.slice(1, EXPLAINED_RIVALS + 1).map((rival) => ({ label: keyLabel(rival), reason: keyRivalReason(winner, rival) }));
|
|
439
452
|
}
|
|
440
453
|
function detectKey(pitches, opts = {}) {
|
|
441
|
-
const asked =
|
|
454
|
+
const asked = _chunkS5BKZZGMcjs.assertRecord.call(void 0, opts, "opts");
|
|
442
455
|
assertPitches(pitches, "key detection pitches", asked.budget);
|
|
443
456
|
const weights = asked.weights;
|
|
444
457
|
if (weights !== void 0 && weights.length !== pitches.length) {
|
|
445
|
-
throw new (0,
|
|
458
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)("weights must have one entry per pitch");
|
|
446
459
|
}
|
|
447
460
|
const profile = resolveKeyProfile(opts.profile);
|
|
448
461
|
const modalCandidates = resolveModalCandidates(opts.modes).map((candidate) => ({
|
|
@@ -453,11 +466,11 @@ function detectKey(pitches, opts = {}) {
|
|
|
453
466
|
let total = 0;
|
|
454
467
|
for (let index = 0; index < pitches.length; index += 1) {
|
|
455
468
|
const weight = weights === void 0 ? 1 : _nullishCoalesce(weights[index], () => ( 0));
|
|
456
|
-
|
|
469
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, weight, `weights[${index}]`);
|
|
457
470
|
if (weight <= 0) {
|
|
458
471
|
continue;
|
|
459
472
|
}
|
|
460
|
-
const pc =
|
|
473
|
+
const pc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(pitches[index], () => ( 0)));
|
|
461
474
|
counts.set(pc, (_nullishCoalesce(counts.get(pc), () => ( 0))) + weight);
|
|
462
475
|
total += weight;
|
|
463
476
|
}
|
|
@@ -538,8 +551,8 @@ function detectKey(pitches, opts = {}) {
|
|
|
538
551
|
return results;
|
|
539
552
|
}
|
|
540
553
|
function detectKeyFromNotes(notes, opts = {}) {
|
|
541
|
-
const asked =
|
|
542
|
-
|
|
554
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
555
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "key detection notes", {
|
|
543
556
|
allowNonPositiveDuration: true,
|
|
544
557
|
budget: asked.budget
|
|
545
558
|
});
|
|
@@ -561,40 +574,40 @@ function detectKeyBest(pitches, opts = {}) {
|
|
|
561
574
|
|
|
562
575
|
// src/analyze/functional/internal.ts
|
|
563
576
|
function isMinorScale(key) {
|
|
564
|
-
return
|
|
577
|
+
return _chunk3R3QVKY5cjs.isMinorMask.call(void 0, key.modeMask12);
|
|
565
578
|
}
|
|
566
579
|
function parallelScale(key) {
|
|
567
580
|
return {
|
|
568
|
-
rootPc:
|
|
569
|
-
modeMask12: isMinorScale(key) ?
|
|
581
|
+
rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc),
|
|
582
|
+
modeMask12: isMinorScale(key) ? _chunk3R3QVKY5cjs.MAJOR_MASK : _chunk3R3QVKY5cjs.NATURAL_MINOR_MASK
|
|
570
583
|
};
|
|
571
584
|
}
|
|
572
585
|
function degreeRootPc(degreeNumber, key) {
|
|
573
|
-
const tones =
|
|
586
|
+
const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key);
|
|
574
587
|
if (tones.length === 0) {
|
|
575
|
-
return
|
|
588
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
576
589
|
}
|
|
577
590
|
const index = ((degreeNumber - 1) % tones.length + tones.length) % tones.length;
|
|
578
|
-
return _nullishCoalesce(tones[index], () => (
|
|
591
|
+
return _nullishCoalesce(tones[index], () => ( _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc)));
|
|
579
592
|
}
|
|
580
593
|
function loweredDegrees(key) {
|
|
581
|
-
const tones =
|
|
594
|
+
const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key);
|
|
582
595
|
const out = /* @__PURE__ */ new Set();
|
|
583
596
|
if (tones.length !== 7) {
|
|
584
597
|
return out;
|
|
585
598
|
}
|
|
586
|
-
const major =
|
|
599
|
+
const major = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.MAJOR_MASK });
|
|
587
600
|
for (let index = 0; index < 7; index += 1) {
|
|
588
601
|
const tone = tones[index];
|
|
589
602
|
const majorTone = major[index];
|
|
590
|
-
if (tone !== void 0 && majorTone !== void 0 &&
|
|
603
|
+
if (tone !== void 0 && majorTone !== void 0 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, majorTone - tone) === 1) {
|
|
591
604
|
out.add(index + 1);
|
|
592
605
|
}
|
|
593
606
|
}
|
|
594
607
|
return out;
|
|
595
608
|
}
|
|
596
609
|
function romanReference(key) {
|
|
597
|
-
return
|
|
610
|
+
return _chunk3R3QVKY5cjs.heptatonicFrameOf.call(void 0, key);
|
|
598
611
|
}
|
|
599
612
|
|
|
600
613
|
// src/analyze/functional/augmented-sixth.ts
|
|
@@ -606,11 +619,11 @@ var TONES_ABOVE_BASS = {
|
|
|
606
619
|
german: [0, 4, 7, 10]
|
|
607
620
|
};
|
|
608
621
|
var SPELLED_ABOVE_BASS = {
|
|
609
|
-
italian: ["P1", "M3", "A6"].map((name) =>
|
|
610
|
-
french: ["P1", "M3", "A4", "A6"].map((name) =>
|
|
611
|
-
german: ["P1", "M3", "P5", "A6"].map((name) =>
|
|
622
|
+
italian: ["P1", "M3", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name)),
|
|
623
|
+
french: ["P1", "M3", "A4", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name)),
|
|
624
|
+
german: ["P1", "M3", "P5", "A6"].map((name) => _chunkS5BKZZGMcjs.parseInterval.call(void 0, name))
|
|
612
625
|
};
|
|
613
|
-
var BASS_INTERVAL =
|
|
626
|
+
var BASS_INTERVAL = _chunkS5BKZZGMcjs.parseInterval.call(void 0, "m6");
|
|
614
627
|
var CHORD_SHAPE = {
|
|
615
628
|
italian: { rootAboveBass: 0, quality: "dom7", intervals: [0, 4, 10] },
|
|
616
629
|
french: { rootAboveBass: 6, quality: "7b5", intervals: [0, 4, 6, 10] },
|
|
@@ -629,7 +642,7 @@ var SYMBOL_BY_KIND = {
|
|
|
629
642
|
german: "Ger6"
|
|
630
643
|
};
|
|
631
644
|
function bassPcOf(key) {
|
|
632
|
-
return
|
|
645
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + BASS_ABOVE_TONIC);
|
|
633
646
|
}
|
|
634
647
|
function bareSpelling(note) {
|
|
635
648
|
return { letter: note.letter, alter: note.alter };
|
|
@@ -643,13 +656,13 @@ function augmentedSixthChord(kind, key) {
|
|
|
643
656
|
return augmentedSixthOn(kind, key);
|
|
644
657
|
}
|
|
645
658
|
function augmentedSixthOn(kind, keyLike) {
|
|
646
|
-
const checked =
|
|
659
|
+
const checked = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
|
|
647
660
|
const shape = CHORD_SHAPE[checked];
|
|
648
|
-
const { scale: key, tonic } =
|
|
661
|
+
const { scale: key, tonic } = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
649
662
|
const bassPc = bassPcOf(key);
|
|
650
|
-
const chord =
|
|
663
|
+
const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bassPc + shape.rootAboveBass), shape.quality, bassPc);
|
|
651
664
|
chord.intervals = [...shape.intervals];
|
|
652
|
-
const bass =
|
|
665
|
+
const bass = _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
|
|
653
666
|
const tones = spelledInChordOrder(checked, tonic);
|
|
654
667
|
const [root = bass] = tones;
|
|
655
668
|
chord.rootSpelling = bareSpelling(root);
|
|
@@ -660,55 +673,55 @@ function augmentedSixthOn(kind, keyLike) {
|
|
|
660
673
|
function kindOfPitchClasses(pcs, bass) {
|
|
661
674
|
for (const kind of KINDS) {
|
|
662
675
|
const tones = TONES_ABOVE_BASS[kind];
|
|
663
|
-
if (pcs.size === tones.length && tones.every((tone) => pcs.has(
|
|
676
|
+
if (pcs.size === tones.length && tones.every((tone) => pcs.has(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bass + tone)))) {
|
|
664
677
|
return kind;
|
|
665
678
|
}
|
|
666
679
|
}
|
|
667
680
|
return null;
|
|
668
681
|
}
|
|
669
682
|
function spellingId(note) {
|
|
670
|
-
return `${
|
|
683
|
+
return `${_chunkS5BKZZGMcjs.diatonicLetterOf.call(void 0, note.letter)}:${note.alter}`;
|
|
671
684
|
}
|
|
672
685
|
function spellsAugmentedSixth(chord, kind, key) {
|
|
673
686
|
const tonic = key.tonic;
|
|
674
|
-
const written = new Set(
|
|
687
|
+
const written = new Set(_chunk3R3QVKY5cjs.spellChord.call(void 0, chord, tonic, key.scale).map(spellingId));
|
|
675
688
|
const tones = spelledFromTonic(kind, tonic).map(spellingId);
|
|
676
689
|
return written.size === tones.length && tones.every((tone) => written.has(tone));
|
|
677
690
|
}
|
|
678
691
|
function augmentedSixthKind(chord, key) {
|
|
679
|
-
return augmentedSixthKindOf(
|
|
692
|
+
return augmentedSixthKindOf(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), key);
|
|
680
693
|
}
|
|
681
694
|
function augmentedSixthKindOf(chord, keyLike) {
|
|
682
|
-
const resolved =
|
|
695
|
+
const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
683
696
|
const bass = bassPcOf(resolved.scale);
|
|
684
|
-
if (
|
|
697
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== bass) {
|
|
685
698
|
return null;
|
|
686
699
|
}
|
|
687
|
-
const kind = kindOfPitchClasses(new Set(
|
|
700
|
+
const kind = kindOfPitchClasses(new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord)), bass);
|
|
688
701
|
if (kind === null || !spellsAugmentedSixth(chord, kind, resolved)) {
|
|
689
702
|
return null;
|
|
690
703
|
}
|
|
691
704
|
return kind;
|
|
692
705
|
}
|
|
693
706
|
function augmentedSixthFromPitchClasses(pcs, bassPc, key) {
|
|
694
|
-
return augmentedSixthOverBass(
|
|
707
|
+
return augmentedSixthOverBass(_chunkS5BKZZGMcjs.assertArray.call(void 0, pcs, "pcs"), bassPc, key);
|
|
695
708
|
}
|
|
696
709
|
function augmentedSixthOverBass(pcs, bassPc, keyLike) {
|
|
697
|
-
const resolved =
|
|
710
|
+
const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
698
711
|
const bass = bassPcOf(resolved.scale);
|
|
699
|
-
if (
|
|
712
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, bassPc) !== bass) {
|
|
700
713
|
return null;
|
|
701
714
|
}
|
|
702
|
-
const kind = kindOfPitchClasses(new Set(pcs.map(
|
|
715
|
+
const kind = kindOfPitchClasses(new Set(pcs.map(_chunkS5BKZZGMcjs.pitchClassOf)), bass);
|
|
703
716
|
return kind === null ? null : augmentedSixthOn(kind, resolved);
|
|
704
717
|
}
|
|
705
718
|
function spellAugmentedSixth(kind, tonic) {
|
|
706
|
-
return spelledFromTonic(kind,
|
|
719
|
+
return spelledFromTonic(kind, _chunkS5BKZZGMcjs.toNoteData.call(void 0, tonic));
|
|
707
720
|
}
|
|
708
721
|
function spelledFromTonic(kind, tonic) {
|
|
709
|
-
const checked =
|
|
710
|
-
const bass =
|
|
711
|
-
return SPELLED_ABOVE_BASS[checked].map((interval) =>
|
|
722
|
+
const checked = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, kind, KINDS, "augmented sixth kind");
|
|
723
|
+
const bass = _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, tonic, BASS_INTERVAL);
|
|
724
|
+
return SPELLED_ABOVE_BASS[checked].map((interval) => _chunkS5BKZZGMcjs.transposeByInterval.call(void 0, bass, interval));
|
|
712
725
|
}
|
|
713
726
|
function augmentedSixthFromSymbol(symbol, key) {
|
|
714
727
|
const kind = KIND_BY_SYMBOL.get(symbol);
|
|
@@ -724,11 +737,11 @@ var SUBDOMINANT_DEGREE = 4;
|
|
|
724
737
|
var DOMINANT_DEGREE = 5;
|
|
725
738
|
var SUBMEDIANT_DEGREE = 6;
|
|
726
739
|
function degreeOffset(degreeNumber, key) {
|
|
727
|
-
return
|
|
740
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, degreeRootPc(degreeNumber, romanReference(key)) - key.rootPc);
|
|
728
741
|
}
|
|
729
742
|
function thirdBelongsToKey(chord, key) {
|
|
730
743
|
return chord.intervals.some(
|
|
731
|
-
(interval) => (
|
|
744
|
+
(interval) => (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 3 || _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 4) && _chunk3R3QVKY5cjs.isScaleTone.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + interval), key)
|
|
732
745
|
);
|
|
733
746
|
}
|
|
734
747
|
function deceptiveTargets(key) {
|
|
@@ -740,25 +753,25 @@ function deceptiveTargets(key) {
|
|
|
740
753
|
return borrowed === submediant ? [submediant] : [submediant, borrowed];
|
|
741
754
|
}
|
|
742
755
|
function restsOnDominant(to, key) {
|
|
743
|
-
if (
|
|
756
|
+
if (_chunk3R3QVKY5cjs.hasMajorThird.call(void 0, to) || _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, to, key)) {
|
|
744
757
|
return true;
|
|
745
758
|
}
|
|
746
|
-
if (
|
|
759
|
+
if (_chunk3R3QVKY5cjs.scaleSystemOf.call(void 0, key) !== "modal") {
|
|
747
760
|
return false;
|
|
748
761
|
}
|
|
749
|
-
const dominantPc =
|
|
750
|
-
const leadingToneDominant =
|
|
762
|
+
const dominantPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + degreeOffset(DOMINANT_DEGREE, key));
|
|
763
|
+
const leadingToneDominant = _chunk3R3QVKY5cjs.isScaleTone.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, dominantPc + 4), key);
|
|
751
764
|
return !leadingToneDominant && thirdBelongsToKey(to, key);
|
|
752
765
|
}
|
|
753
766
|
function isTonicSixFour(chord, key) {
|
|
754
|
-
if (
|
|
767
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) !== 0 || chord.bassPc === void 0) {
|
|
755
768
|
return false;
|
|
756
769
|
}
|
|
757
|
-
const isTriad = chord.intervals.length === 3 && chord.intervals.some((interval) =>
|
|
758
|
-
return isTriad &&
|
|
770
|
+
const isTriad = chord.intervals.length === 3 && chord.intervals.some((interval) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, interval) === 7);
|
|
771
|
+
return isTriad && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.bassPc - chord.rootPc) === 7;
|
|
759
772
|
}
|
|
760
773
|
function isCadentialSixFour(chord, next, key) {
|
|
761
|
-
return isTonicSixFour(chord, key) &&
|
|
774
|
+
return isTonicSixFour(chord, key) && _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, next, key) && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(next.bassPc, () => ( next.rootPc)));
|
|
762
775
|
}
|
|
763
776
|
function outerPitches(voicing) {
|
|
764
777
|
let bass;
|
|
@@ -777,20 +790,28 @@ function outerPitches(voicing) {
|
|
|
777
790
|
return { bass, soprano };
|
|
778
791
|
}
|
|
779
792
|
function bassPcOf2(chord, voicedBass) {
|
|
780
|
-
return
|
|
793
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(_nullishCoalesce(voicedBass, () => ( chord.bassPc)), () => ( chord.rootPc)));
|
|
781
794
|
}
|
|
795
|
+
var CADENCE_TYPES = Object.freeze([
|
|
796
|
+
"authentic",
|
|
797
|
+
"plagal",
|
|
798
|
+
"half",
|
|
799
|
+
"deceptive",
|
|
800
|
+
"phrygian",
|
|
801
|
+
"modal"
|
|
802
|
+
]);
|
|
782
803
|
function cadenceType(from, to, key, fromBassPc, toBassPc) {
|
|
783
|
-
const tonic =
|
|
784
|
-
const fromOffset =
|
|
785
|
-
const toOffset =
|
|
786
|
-
const dominant =
|
|
787
|
-
if ((dominant ||
|
|
804
|
+
const tonic = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
805
|
+
const fromOffset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc - tonic);
|
|
806
|
+
const toOffset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - tonic);
|
|
807
|
+
const dominant = _chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, from, key);
|
|
808
|
+
if ((dominant || _chunk3R3QVKY5cjs.isLeadingToneChordOf.call(void 0, from, key)) && toOffset === 0) {
|
|
788
809
|
return "authentic";
|
|
789
810
|
}
|
|
790
811
|
if (fromOffset === degreeOffset(SUBDOMINANT_DEGREE, key) && toOffset === 0) {
|
|
791
812
|
return "plagal";
|
|
792
813
|
}
|
|
793
|
-
if (fromOffset === 10 && toOffset === 0 &&
|
|
814
|
+
if (fromOffset === 10 && toOffset === 0 && _chunk3R3QVKY5cjs.hasMajorThird.call(void 0, from)) {
|
|
794
815
|
return "modal";
|
|
795
816
|
}
|
|
796
817
|
if (dominant && deceptiveTargets(key).includes(toOffset)) {
|
|
@@ -798,7 +819,7 @@ function cadenceType(from, to, key, fromBassPc, toBassPc) {
|
|
|
798
819
|
}
|
|
799
820
|
const dominantOffset = degreeOffset(DOMINANT_DEGREE, key);
|
|
800
821
|
const half = toOffset === dominantOffset && restsOnDominant(to, key) && fromOffset !== dominantOffset;
|
|
801
|
-
if (half && isMinorScale(key) && fromOffset === 5 &&
|
|
822
|
+
if (half && isMinorScale(key) && fromOffset === 5 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, fromBassPc - tonic) === 8 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, toBassPc - tonic) === 7) {
|
|
802
823
|
return "phrygian";
|
|
803
824
|
}
|
|
804
825
|
return half ? "half" : null;
|
|
@@ -900,12 +921,12 @@ function cadenceAlternatives(facts) {
|
|
|
900
921
|
return out;
|
|
901
922
|
}
|
|
902
923
|
function detectCadence(from, to, key, opts = {}) {
|
|
903
|
-
const { approach, ...rest } =
|
|
924
|
+
const { approach, ...rest } = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
904
925
|
return cadenceBetween(
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
approach === void 0 ? rest : { ...rest, approach:
|
|
926
|
+
_chunk3R3QVKY5cjs.toChordData.call(void 0, from),
|
|
927
|
+
_chunk3R3QVKY5cjs.toChordData.call(void 0, to),
|
|
928
|
+
_chunk3R3QVKY5cjs.toKeyScale.call(void 0, key),
|
|
929
|
+
approach === void 0 ? rest : { ...rest, approach: _chunk3R3QVKY5cjs.toChordData.call(void 0, approach) }
|
|
909
930
|
);
|
|
910
931
|
}
|
|
911
932
|
function cadenceBetween(from, to, key, opts = {}) {
|
|
@@ -914,11 +935,11 @@ function cadenceBetween(from, to, key, opts = {}) {
|
|
|
914
935
|
const toOuter = outerPitches(_nullishCoalesce(toVoicing, () => ( [])));
|
|
915
936
|
const fromBassPc = bassPcOf2(from, fromOuter.bass);
|
|
916
937
|
const toBassPc = bassPcOf2(to, toOuter.bass);
|
|
917
|
-
const toRootPosition = toBassPc ===
|
|
918
|
-
const rootPosition = fromBassPc ===
|
|
919
|
-
const soprano = toOuter.soprano === void 0 ? null :
|
|
938
|
+
const toRootPosition = toBassPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc);
|
|
939
|
+
const rootPosition = fromBassPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc) && toRootPosition;
|
|
940
|
+
const soprano = toOuter.soprano === void 0 ? null : _chunk3R3QVKY5cjs.chordToneRole.call(void 0, toOuter.soprano, to);
|
|
920
941
|
const type = cadenceType(from, to, key, fromBassPc, toBassPc);
|
|
921
|
-
const leadingTone =
|
|
942
|
+
const leadingTone = _chunk3R3QVKY5cjs.isLeadingToneChordOf.call(void 0, from, key);
|
|
922
943
|
const facts = {
|
|
923
944
|
type,
|
|
924
945
|
strength: type === "authentic" ? authenticStrength(leadingTone, rootPosition, toOuter.soprano !== void 0, soprano) : null,
|
|
@@ -927,10 +948,10 @@ function cadenceBetween(from, to, key, opts = {}) {
|
|
|
927
948
|
// promised is the one it does not get.
|
|
928
949
|
evaded: type === "authentic" && !toRootPosition,
|
|
929
950
|
leadingTone,
|
|
930
|
-
fromOffset:
|
|
931
|
-
toOffset:
|
|
951
|
+
fromOffset: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, from.rootPc - key.rootPc),
|
|
952
|
+
toOffset: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc),
|
|
932
953
|
minor: isMinorScale(key),
|
|
933
|
-
onFifthDegree:
|
|
954
|
+
onFifthDegree: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, to.rootPc - key.rootPc) === degreeOffset(DOMINANT_DEGREE, key),
|
|
934
955
|
sixFour: opts.approach !== void 0 && isCadentialSixFour(opts.approach, from, key),
|
|
935
956
|
withinSixFour: isCadentialSixFour(from, to, key)
|
|
936
957
|
};
|
|
@@ -951,53 +972,53 @@ function cadenceBetween(from, to, key, opts = {}) {
|
|
|
951
972
|
// src/analyze/functional/borrowed.ts
|
|
952
973
|
function isMinorScaleAlteration(chord, key) {
|
|
953
974
|
if (!isMinorScale(key)) return false;
|
|
954
|
-
const alteredKey = { rootPc:
|
|
955
|
-
if (
|
|
956
|
-
return
|
|
975
|
+
const alteredKey = { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.HARMONIC_MINOR_MASK };
|
|
976
|
+
if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, alteredKey)) return true;
|
|
977
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 11 && _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, { rootPc: _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunk3R3QVKY5cjs.MELODIC_MINOR_MASK });
|
|
957
978
|
}
|
|
958
979
|
function isBorrowed(chord, key) {
|
|
959
|
-
if (
|
|
980
|
+
if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
|
|
960
981
|
return false;
|
|
961
982
|
}
|
|
962
|
-
if (
|
|
983
|
+
if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
|
|
963
984
|
return true;
|
|
964
985
|
}
|
|
965
986
|
if (isMinorScaleAlteration(chord, key)) {
|
|
966
987
|
return false;
|
|
967
988
|
}
|
|
968
|
-
return
|
|
989
|
+
return _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, parallelScale(key));
|
|
969
990
|
}
|
|
970
991
|
function borrowedSourceOf(chord, key) {
|
|
971
|
-
if (
|
|
992
|
+
if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
|
|
972
993
|
return null;
|
|
973
994
|
}
|
|
974
|
-
if (
|
|
995
|
+
if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) return "neapolitan";
|
|
975
996
|
if (isBorrowed(chord, key)) {
|
|
976
997
|
return isMinorScale(key) ? "parallelMajor" : "parallelMinor";
|
|
977
998
|
}
|
|
978
999
|
return null;
|
|
979
1000
|
}
|
|
980
1001
|
function isBorrowedChord(chord, key) {
|
|
981
|
-
return isBorrowed(
|
|
1002
|
+
return isBorrowed(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
|
|
982
1003
|
}
|
|
983
1004
|
function borrowedSource(chord, key) {
|
|
984
|
-
return borrowedSourceOf(
|
|
1005
|
+
return borrowedSourceOf(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
|
|
985
1006
|
}
|
|
986
1007
|
|
|
987
1008
|
// src/analyze/functional/tonicization.ts
|
|
988
1009
|
function isTonicizableRoot(rootPc, key) {
|
|
989
|
-
return
|
|
1010
|
+
return _chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key).some((degree) => degree.rootPc === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc));
|
|
990
1011
|
}
|
|
991
1012
|
function appliedTarget(chord, key) {
|
|
992
|
-
const dominant =
|
|
993
|
-
const leadingTone =
|
|
1013
|
+
const dominant = _chunk3R3QVKY5cjs.hasDominantSonority.call(void 0, chord);
|
|
1014
|
+
const leadingTone = _chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(chord.quality);
|
|
994
1015
|
if (!dominant && !leadingTone) {
|
|
995
1016
|
return null;
|
|
996
1017
|
}
|
|
997
|
-
return
|
|
1018
|
+
return _chunk3R3QVKY5cjs.appliedDominantTarget.call(void 0, chord.rootPc + (dominant ? 5 : 1), key);
|
|
998
1019
|
}
|
|
999
1020
|
function isAppliedDominant(chord, key) {
|
|
1000
|
-
if (
|
|
1021
|
+
if (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
|
|
1001
1022
|
return false;
|
|
1002
1023
|
}
|
|
1003
1024
|
const source = borrowedSourceOf(chord, key);
|
|
@@ -1010,7 +1031,7 @@ function pointsAtTonicizableDegree(chord, key) {
|
|
|
1010
1031
|
if (appliedTarget(chord, key) !== null) {
|
|
1011
1032
|
return true;
|
|
1012
1033
|
}
|
|
1013
|
-
return
|
|
1034
|
+
return _chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, chord) && isTonicizableRoot(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - 1), key);
|
|
1014
1035
|
}
|
|
1015
1036
|
|
|
1016
1037
|
// src/analyze/functional/roman.ts
|
|
@@ -1125,33 +1146,33 @@ function parseInversion(figures) {
|
|
|
1125
1146
|
function parseSimpleRoman(text, key) {
|
|
1126
1147
|
const match = /^([b#]?)([iIvV]+)(.*)$/.exec(text.trim());
|
|
1127
1148
|
if (!match) {
|
|
1128
|
-
throw new (0,
|
|
1149
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
|
|
1129
1150
|
}
|
|
1130
1151
|
const glyphText = _nullishCoalesce(match[2], () => ( ""));
|
|
1131
1152
|
if (glyphText !== glyphText.toUpperCase() && glyphText !== glyphText.toLowerCase()) {
|
|
1132
|
-
throw new (0,
|
|
1153
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Roman numeral must use one case: ${text}`);
|
|
1133
1154
|
}
|
|
1134
1155
|
const accidental = match[1] === "b" ? -1 : match[1] === "#" ? 1 : 0;
|
|
1135
1156
|
const glyph = glyphText.toUpperCase();
|
|
1136
1157
|
const degreeNumber = ROMAN_TO_DEGREE[glyph];
|
|
1137
1158
|
if (degreeNumber === void 0) {
|
|
1138
|
-
throw new (0,
|
|
1159
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
|
|
1139
1160
|
}
|
|
1140
1161
|
const isUpper = glyphText[0] === _optionalChain([glyphText, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.toUpperCase, 'call', _7 => _7()]);
|
|
1141
1162
|
const suffix = _nullishCoalesce(match[3], () => ( ""));
|
|
1142
1163
|
const frame = romanReference(key);
|
|
1143
1164
|
const redundantFlat = accidental === -1 && loweredDegrees(frame).has(degreeNumber);
|
|
1144
|
-
const diatonicRootPc =
|
|
1165
|
+
const diatonicRootPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0,
|
|
1145
1166
|
degreeRootPc(degreeNumber, frame) + (redundantFlat ? 0 : accidental)
|
|
1146
1167
|
);
|
|
1147
|
-
const rootForQuality = (quality2) => accidental === 0 && degreeNumber === 7 && loweredDegrees(frame).has(7) &&
|
|
1168
|
+
const rootForQuality = (quality2) => accidental === 0 && degreeNumber === 7 && loweredDegrees(frame).has(7) && _chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(quality2) ? _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, diatonicRootPc + 1) : diatonicRootPc;
|
|
1148
1169
|
const canonical = SUFFIX_QUALITY.get(`${isUpper ? "u" : "l"}:${suffix}`);
|
|
1149
1170
|
if (canonical !== void 0) {
|
|
1150
1171
|
return { rootPc: rootForQuality(canonical), quality: canonical, inversion: 0 };
|
|
1151
1172
|
}
|
|
1152
1173
|
const parsedSuffix = /^(o|°|dim|ø|\+|aug)?(maj7|M7)?(65|64|43|42|7|6|2)?$/.exec(suffix);
|
|
1153
1174
|
if (!parsedSuffix) {
|
|
1154
|
-
throw new (0,
|
|
1175
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
|
|
1155
1176
|
}
|
|
1156
1177
|
const qualityMarker = _nullishCoalesce(parsedSuffix[1], () => ( ""));
|
|
1157
1178
|
const isDim = qualityMarker === "o" || qualityMarker === "\xB0" || qualityMarker === "dim";
|
|
@@ -1160,7 +1181,7 @@ function parseSimpleRoman(text, key) {
|
|
|
1160
1181
|
const explicitMaj7 = parsedSuffix[2] !== void 0;
|
|
1161
1182
|
const figures = _nullishCoalesce(parsedSuffix[3], () => ( ""));
|
|
1162
1183
|
if (figures !== "" && !INVERSION_FIGURES.has(figures)) {
|
|
1163
|
-
throw new (0,
|
|
1184
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
1164
1185
|
`Unsupported figured-bass or extension "${figures}" in Roman numeral: ${text}`
|
|
1165
1186
|
);
|
|
1166
1187
|
}
|
|
@@ -1191,22 +1212,22 @@ function parseSimpleRoman(text, key) {
|
|
|
1191
1212
|
var NEAPOLITAN_SIXTH = "N6";
|
|
1192
1213
|
function chromaticSymbolChord(text, key) {
|
|
1193
1214
|
if (text === NEAPOLITAN_SIXTH) {
|
|
1194
|
-
const rootPc =
|
|
1195
|
-
return
|
|
1215
|
+
const rootPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.scale.rootPc + 1);
|
|
1216
|
+
return _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, "maj", _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc + 4));
|
|
1196
1217
|
}
|
|
1197
1218
|
return augmentedSixthFromSymbol(text, key);
|
|
1198
1219
|
}
|
|
1199
1220
|
function tonicizedKey(targetRoot, key) {
|
|
1200
|
-
return
|
|
1201
|
-
scale:
|
|
1202
|
-
tonic:
|
|
1221
|
+
return _chunk3R3QVKY5cjs.resolveKey.call(void 0, {
|
|
1222
|
+
scale: _chunk3R3QVKY5cjs.majorKey.call(void 0, targetRoot),
|
|
1223
|
+
tonic: _chunk3R3QVKY5cjs.spellPitchClass.call(void 0, targetRoot, key.tonic, key.scale)
|
|
1203
1224
|
});
|
|
1204
1225
|
}
|
|
1205
1226
|
function augmentedSixthTarget(chord, key) {
|
|
1206
|
-
if (
|
|
1227
|
+
if (_chunk3R3QVKY5cjs.chordToneSpellings.call(void 0, chord) === void 0) {
|
|
1207
1228
|
return null;
|
|
1208
1229
|
}
|
|
1209
|
-
for (const degree of
|
|
1230
|
+
for (const degree of _chunk3R3QVKY5cjs.tonicizableDegrees.call(void 0, key.scale)) {
|
|
1210
1231
|
if (degree.degreeNumber === 1) {
|
|
1211
1232
|
continue;
|
|
1212
1233
|
}
|
|
@@ -1217,14 +1238,14 @@ function augmentedSixthTarget(chord, key) {
|
|
|
1217
1238
|
return null;
|
|
1218
1239
|
}
|
|
1219
1240
|
function chordFromParsed(parsed) {
|
|
1220
|
-
const chord =
|
|
1241
|
+
const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, parsed.rootPc, parsed.quality);
|
|
1221
1242
|
if (parsed.inversion > 0 && parsed.inversion < chord.intervals.length) {
|
|
1222
|
-
chord.bassPc =
|
|
1243
|
+
chord.bassPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, parsed.rootPc + (_nullishCoalesce(chord.intervals[parsed.inversion], () => ( 0))));
|
|
1223
1244
|
}
|
|
1224
1245
|
return chord;
|
|
1225
1246
|
}
|
|
1226
1247
|
function romanToChord(text, key) {
|
|
1227
|
-
const resolved =
|
|
1248
|
+
const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, key);
|
|
1228
1249
|
const scale = resolved.scale;
|
|
1229
1250
|
const trimmed = text.trim().replace(/(\d)\/(?=\d)/g, "$1");
|
|
1230
1251
|
const slash = trimmed.indexOf("/");
|
|
@@ -1241,14 +1262,14 @@ function romanStyle(quality) {
|
|
|
1241
1262
|
return ROMAN_STYLE[quality];
|
|
1242
1263
|
}
|
|
1243
1264
|
function romanSpelling(rootPc, key) {
|
|
1244
|
-
const tones =
|
|
1245
|
-
const diatonic = tones.indexOf(
|
|
1265
|
+
const tones = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
|
|
1266
|
+
const diatonic = tones.indexOf(_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc));
|
|
1246
1267
|
if (diatonic >= 0) {
|
|
1247
1268
|
return { degreeNumber: diatonic + 1, accidental: "" };
|
|
1248
1269
|
}
|
|
1249
1270
|
const lowered = loweredDegrees(romanReference(key));
|
|
1250
1271
|
for (let i = 0; i < tones.length; i += 1) {
|
|
1251
|
-
if (lowered.has(i + 1) &&
|
|
1272
|
+
if (lowered.has(i + 1) && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
|
|
1252
1273
|
return { degreeNumber: i + 1, accidental: "#" };
|
|
1253
1274
|
}
|
|
1254
1275
|
}
|
|
@@ -1257,20 +1278,20 @@ function romanSpelling(rootPc, key) {
|
|
|
1257
1278
|
if (degreeNumber2 === 1 || degreeNumber2 === 5) {
|
|
1258
1279
|
continue;
|
|
1259
1280
|
}
|
|
1260
|
-
if (
|
|
1281
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) - 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
|
|
1261
1282
|
return { degreeNumber: degreeNumber2, accidental: "b" };
|
|
1262
1283
|
}
|
|
1263
1284
|
}
|
|
1264
1285
|
for (let i = 0; i < tones.length; i += 1) {
|
|
1265
|
-
if (
|
|
1286
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc)) {
|
|
1266
1287
|
return { degreeNumber: i + 1, accidental: "#" };
|
|
1267
1288
|
}
|
|
1268
1289
|
}
|
|
1269
|
-
const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[
|
|
1290
|
+
const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, rootPc - key.rootPc)], () => ( [1, ""]));
|
|
1270
1291
|
return { degreeNumber, accidental };
|
|
1271
1292
|
}
|
|
1272
1293
|
function seventhDegreeAccidental(accidental, degreeNumber, quality, key) {
|
|
1273
|
-
if (degreeNumber !== 7 || !
|
|
1294
|
+
if (degreeNumber !== 7 || !_chunk3R3QVKY5cjs.LEADING_TONE_QUALITIES.has(quality) || !loweredDegrees(romanReference(key)).has(7)) {
|
|
1274
1295
|
return accidental;
|
|
1275
1296
|
}
|
|
1276
1297
|
if (accidental === "#") {
|
|
@@ -1312,22 +1333,22 @@ var SEVENTH_FIGURE_QUALITIES = /* @__PURE__ */ new Set([
|
|
|
1312
1333
|
function numeralFor(degreeNumber, lower) {
|
|
1313
1334
|
const glyph = ROMAN[degreeNumber - 1];
|
|
1314
1335
|
if (glyph === void 0) {
|
|
1315
|
-
throw new (0,
|
|
1336
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`no Roman numeral for scale degree ${degreeNumber}`);
|
|
1316
1337
|
}
|
|
1317
1338
|
return lower ? glyph.toLowerCase() : glyph;
|
|
1318
1339
|
}
|
|
1319
1340
|
function chordToRoman(chord, key, opts = {}) {
|
|
1320
|
-
return renderRoman(
|
|
1341
|
+
return renderRoman(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), key, _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts")).roman;
|
|
1321
1342
|
}
|
|
1322
1343
|
function renderRoman(chord, keyLike, opts) {
|
|
1323
|
-
const resolved =
|
|
1344
|
+
const resolved = _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
1324
1345
|
const key = resolved.scale;
|
|
1325
1346
|
const augmented = augmentedSixthSymbol(chord, resolved);
|
|
1326
1347
|
if (augmented !== null) {
|
|
1327
1348
|
return { roman: augmented, derivation: { kind: "augmentedSixth" } };
|
|
1328
1349
|
}
|
|
1329
1350
|
if (opts.applied === true) {
|
|
1330
|
-
const target = _nullishCoalesce(augmentedSixthTarget(chord, resolved), () => ( (
|
|
1351
|
+
const target = _nullishCoalesce(augmentedSixthTarget(chord, resolved), () => ( (_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key) ? null : appliedTarget(chord, key))));
|
|
1331
1352
|
if (target !== null) {
|
|
1332
1353
|
const local = renderRoman(chord, tonicizedKey(target.rootPc, resolved), {}).roman;
|
|
1333
1354
|
const numeral = numeralFor(target.degreeNumber, target.lower);
|
|
@@ -1344,14 +1365,14 @@ function renderRoman(chord, keyLike, opts) {
|
|
|
1344
1365
|
let inversion = 0;
|
|
1345
1366
|
let bass = "root";
|
|
1346
1367
|
if (chord.bassPc !== void 0) {
|
|
1347
|
-
const idx = chord.intervals.findIndex((iv) =>
|
|
1368
|
+
const idx = chord.intervals.findIndex((iv) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === chord.bassPc);
|
|
1348
1369
|
if (idx > 0) {
|
|
1349
1370
|
inversion = idx;
|
|
1350
1371
|
} else if (idx < 0) {
|
|
1351
1372
|
bass = "foreign";
|
|
1352
1373
|
}
|
|
1353
1374
|
}
|
|
1354
|
-
if (opts.neapolitan === true && inversion === 1 &&
|
|
1375
|
+
if (opts.neapolitan === true && inversion === 1 && _chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
|
|
1355
1376
|
return { roman: NEAPOLITAN_SIXTH, derivation: { kind: "neapolitan" } };
|
|
1356
1377
|
}
|
|
1357
1378
|
if (inversion > 0) {
|
|
@@ -1450,8 +1471,8 @@ function romanAlternatives(chord, key, opts) {
|
|
|
1450
1471
|
return out;
|
|
1451
1472
|
}
|
|
1452
1473
|
function explainRoman(chord, key, opts = {}) {
|
|
1453
|
-
const asked =
|
|
1454
|
-
const data =
|
|
1474
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
1475
|
+
const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
|
|
1455
1476
|
const { roman, derivation } = renderRoman(data, key, asked);
|
|
1456
1477
|
return {
|
|
1457
1478
|
roman,
|
|
@@ -1466,6 +1487,7 @@ function capitalize(text) {
|
|
|
1466
1487
|
}
|
|
1467
1488
|
|
|
1468
1489
|
// src/analyze/functional/function.ts
|
|
1490
|
+
var HARMONIC_FUNCTIONS = Object.freeze(["tonic", "subdominant", "dominant"]);
|
|
1469
1491
|
var FUNCTION_BY_OFFSET = [
|
|
1470
1492
|
"tonic",
|
|
1471
1493
|
// 0 I
|
|
@@ -1519,18 +1541,18 @@ var MINOR_FUNCTION_BY_OFFSET = [
|
|
|
1519
1541
|
// 11 viio
|
|
1520
1542
|
];
|
|
1521
1543
|
function isMinorKey(key) {
|
|
1522
|
-
return isMinorScale(
|
|
1544
|
+
return isMinorScale(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
|
|
1523
1545
|
}
|
|
1524
1546
|
function functionOf(chord, key) {
|
|
1525
|
-
return functionWithReason(
|
|
1547
|
+
return functionWithReason(_chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key)).function;
|
|
1526
1548
|
}
|
|
1527
1549
|
function degreeFunction(chord, key) {
|
|
1528
1550
|
const functions = isMinorScale(key) ? MINOR_FUNCTION_BY_OFFSET : FUNCTION_BY_OFFSET;
|
|
1529
|
-
return _nullishCoalesce(functions[
|
|
1551
|
+
return _nullishCoalesce(functions[_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc)], () => ( "tonic"));
|
|
1530
1552
|
}
|
|
1531
1553
|
function functionWithReason(chord, key) {
|
|
1532
|
-
const offset =
|
|
1533
|
-
if (
|
|
1554
|
+
const offset = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc);
|
|
1555
|
+
if (_chunk3R3QVKY5cjs.isNeapolitanChordOf.call(void 0, chord, key)) {
|
|
1534
1556
|
return { function: "subdominant", reason: "neapolitan" };
|
|
1535
1557
|
}
|
|
1536
1558
|
if (augmentedSixthKindOf(chord, key) !== null) {
|
|
@@ -1539,7 +1561,7 @@ function functionWithReason(chord, key) {
|
|
|
1539
1561
|
if (isAppliedDominant(chord, key)) {
|
|
1540
1562
|
return { function: "dominant", reason: "applied" };
|
|
1541
1563
|
}
|
|
1542
|
-
if (!isMinorScale(key) && !
|
|
1564
|
+
if (!isMinorScale(key) && !_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key) && _chunk3R3QVKY5cjs.hasMajorThird.call(void 0, chord) && (offset === 8 || offset === 10)) {
|
|
1543
1565
|
return { function: "subdominant", reason: "flatSideMajor" };
|
|
1544
1566
|
}
|
|
1545
1567
|
return { function: degreeFunction(chord, key), reason: "degree" };
|
|
@@ -1562,7 +1584,7 @@ function describeChord(fn, reason, roman, source) {
|
|
|
1562
1584
|
function functionAlternatives(chord, key, reason, accepted) {
|
|
1563
1585
|
const out = [];
|
|
1564
1586
|
if (reason === "degree") {
|
|
1565
|
-
if (pointsAtTonicizableDegree(chord, key) &&
|
|
1587
|
+
if (pointsAtTonicizableDegree(chord, key) && _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, key)) {
|
|
1566
1588
|
out.push({
|
|
1567
1589
|
label: "applied dominant",
|
|
1568
1590
|
reason: "The chord is diatonic to the key, so it keeps the function of its degree rather than tonicizing another"
|
|
@@ -1586,15 +1608,15 @@ function functionAlternatives(chord, key, reason, accepted) {
|
|
|
1586
1608
|
return out;
|
|
1587
1609
|
}
|
|
1588
1610
|
function isDiatonic(chord, key) {
|
|
1589
|
-
return
|
|
1611
|
+
return _chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, _chunk3R3QVKY5cjs.toChordData.call(void 0, chord), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
|
|
1590
1612
|
}
|
|
1591
1613
|
function parallelKey(key) {
|
|
1592
|
-
return parallelScale(
|
|
1614
|
+
return parallelScale(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, key));
|
|
1593
1615
|
}
|
|
1594
1616
|
function analyzeChord(chord, key, opts = {}) {
|
|
1595
|
-
const asked =
|
|
1596
|
-
const data =
|
|
1597
|
-
const scale =
|
|
1617
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
1618
|
+
const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, chord);
|
|
1619
|
+
const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
|
|
1598
1620
|
const source = borrowedSourceOf(data, scale);
|
|
1599
1621
|
const { function: fn, reason } = functionWithReason(data, scale);
|
|
1600
1622
|
const roman = renderRoman(data, scale, asked).roman;
|
|
@@ -1611,18 +1633,18 @@ function analyzeChord(chord, key, opts = {}) {
|
|
|
1611
1633
|
};
|
|
1612
1634
|
}
|
|
1613
1635
|
function secondaryDominant(targetDegree, key) {
|
|
1614
|
-
const scale =
|
|
1615
|
-
const degreeCount =
|
|
1616
|
-
|
|
1636
|
+
const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
|
|
1637
|
+
const degreeCount = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, scale).length;
|
|
1638
|
+
_chunkS5BKZZGMcjs.assertInteger.call(void 0, targetDegree, "targetDegree", 1, degreeCount);
|
|
1617
1639
|
const targetRoot = degreeRootPc(targetDegree, scale);
|
|
1618
|
-
return
|
|
1640
|
+
return _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, targetRoot + 7), "dom7");
|
|
1619
1641
|
}
|
|
1620
1642
|
function secondaryDominantOf(target) {
|
|
1621
|
-
const data =
|
|
1622
|
-
|
|
1623
|
-
const dominant =
|
|
1643
|
+
const data = _chunk3R3QVKY5cjs.toChordData.call(void 0, target);
|
|
1644
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, data.rootPc, "target chord rootPc");
|
|
1645
|
+
const dominant = _chunk3R3QVKY5cjs.makeChord.call(void 0, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, data.rootPc + 7), "dom7");
|
|
1624
1646
|
if (data.rootSpelling !== void 0) {
|
|
1625
|
-
const moved =
|
|
1647
|
+
const moved = _chunkS5BKZZGMcjs.transposeNote.call(void 0, data.rootSpelling, 7);
|
|
1626
1648
|
dominant.rootSpelling = { letter: moved.letter, alter: moved.alter };
|
|
1627
1649
|
}
|
|
1628
1650
|
return dominant;
|
|
@@ -1631,17 +1653,17 @@ function secondaryDominantOf(target) {
|
|
|
1631
1653
|
// src/analyze/functional/pivot.ts
|
|
1632
1654
|
var HEPTATONIC_DEGREES = 7;
|
|
1633
1655
|
function pivotChords(from, to) {
|
|
1634
|
-
return pivotsBetween(
|
|
1656
|
+
return pivotsBetween(_chunk3R3QVKY5cjs.toKeyScale.call(void 0, from), _chunk3R3QVKY5cjs.toKeyScale.call(void 0, to));
|
|
1635
1657
|
}
|
|
1636
1658
|
function pivotsBetween(from, to) {
|
|
1637
|
-
if (
|
|
1659
|
+
if (_chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, from).length !== HEPTATONIC_DEGREES) {
|
|
1638
1660
|
return [];
|
|
1639
1661
|
}
|
|
1640
1662
|
const pivots = [];
|
|
1641
1663
|
const seen = /* @__PURE__ */ new Set();
|
|
1642
1664
|
for (let degree = 1; degree <= HEPTATONIC_DEGREES; degree += 1) {
|
|
1643
1665
|
const chord = stackedTriad(degree, from);
|
|
1644
|
-
if (chord === null || !
|
|
1666
|
+
if (chord === null || !_chunk3R3QVKY5cjs.isDiatonicChord.call(void 0, chord, to)) {
|
|
1645
1667
|
continue;
|
|
1646
1668
|
}
|
|
1647
1669
|
const identity = `${chord.rootPc}:${chord.quality}`;
|
|
@@ -1659,9 +1681,9 @@ function pivotsBetween(from, to) {
|
|
|
1659
1681
|
}
|
|
1660
1682
|
function stackedTriad(degree, key) {
|
|
1661
1683
|
try {
|
|
1662
|
-
return
|
|
1684
|
+
return _chunk3R3QVKY5cjs.diatonicTriad.call(void 0, degree, key);
|
|
1663
1685
|
} catch (error) {
|
|
1664
|
-
if (error instanceof
|
|
1686
|
+
if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
|
|
1665
1687
|
return null;
|
|
1666
1688
|
}
|
|
1667
1689
|
throw error;
|
|
@@ -1669,6 +1691,16 @@ function stackedTriad(degree, key) {
|
|
|
1669
1691
|
}
|
|
1670
1692
|
|
|
1671
1693
|
// src/analyze/form/internal.ts
|
|
1694
|
+
function barSpanOf(startBeat, endBeat, meter) {
|
|
1695
|
+
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meter);
|
|
1696
|
+
const lastBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, endBeat, meter);
|
|
1697
|
+
const partOf = (beat, bar) => {
|
|
1698
|
+
const barStart = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
|
|
1699
|
+
const barLength = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, barStart, meter);
|
|
1700
|
+
return barLength > 0 ? (beat - barStart) / barLength : 0;
|
|
1701
|
+
};
|
|
1702
|
+
return lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);
|
|
1703
|
+
}
|
|
1672
1704
|
function clamp01(value) {
|
|
1673
1705
|
return Math.min(1, Math.max(0, value));
|
|
1674
1706
|
}
|
|
@@ -1683,34 +1715,34 @@ function firstSoundingBeat(notes, fallback = 0) {
|
|
|
1683
1715
|
return Number.isFinite(first) ? first : fallback;
|
|
1684
1716
|
}
|
|
1685
1717
|
function lastBarOf(meter, spanStart, spanEnd) {
|
|
1686
|
-
const firstBar =
|
|
1687
|
-
const bar =
|
|
1688
|
-
if (bar > firstBar &&
|
|
1718
|
+
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, spanStart, meter);
|
|
1719
|
+
const bar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(spanStart, spanEnd), meter);
|
|
1720
|
+
if (bar > firstBar && _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter) >= spanEnd - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1689
1721
|
return bar - 1;
|
|
1690
1722
|
}
|
|
1691
1723
|
return bar;
|
|
1692
1724
|
}
|
|
1693
1725
|
function sliceBars(notes, meter, spanStart, spanEnd, budget) {
|
|
1694
|
-
const firstBar =
|
|
1726
|
+
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, spanStart, meter);
|
|
1695
1727
|
const lastBar = lastBarOf(meter, spanStart, spanEnd);
|
|
1696
1728
|
const barCount = lastBar - firstBar + 1;
|
|
1697
|
-
|
|
1729
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, barCount, "form bars", budget);
|
|
1698
1730
|
const buckets = Array.from({ length: barCount }, () => []);
|
|
1699
1731
|
let memberships = 0;
|
|
1700
1732
|
for (const note of notes) {
|
|
1701
1733
|
const noteEnd = note.startBeat + note.durationBeat;
|
|
1702
|
-
const from = Math.max(firstBar,
|
|
1703
|
-
const to = Math.min(lastBar,
|
|
1734
|
+
const from = Math.max(firstBar, _chunkS5BKZZGMcjs.barIndexAt.call(void 0, note.startBeat, meter));
|
|
1735
|
+
const to = Math.min(lastBar, _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(note.startBeat, noteEnd - _chunkS5BKZZGMcjs.BEAT_EPS), meter));
|
|
1704
1736
|
memberships += Math.max(0, to - from + 1);
|
|
1705
|
-
|
|
1737
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, memberships, "form note-to-bar memberships", budget);
|
|
1706
1738
|
for (let bar = from; bar <= to; bar += 1) {
|
|
1707
1739
|
_optionalChain([buckets, 'access', _8 => _8[bar - firstBar], 'optionalAccess', _9 => _9.push, 'call', _10 => _10(note)]);
|
|
1708
1740
|
}
|
|
1709
1741
|
}
|
|
1710
1742
|
const slices = [];
|
|
1711
1743
|
for (let bar = firstBar; bar <= lastBar; bar += 1) {
|
|
1712
|
-
const startBeat =
|
|
1713
|
-
const endBeat =
|
|
1744
|
+
const startBeat = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meter);
|
|
1745
|
+
const endBeat = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: bar + 1, beat: 0 }, meter);
|
|
1714
1746
|
const barNotes = _nullishCoalesce(buckets[bar - firstBar], () => ( []));
|
|
1715
1747
|
const { weights, totalWeight } = windowWeights(barNotes, startBeat, endBeat, meter);
|
|
1716
1748
|
slices.push({ index: bar, startBeat, endBeat, notes: barNotes, weights, totalWeight });
|
|
@@ -1728,10 +1760,10 @@ function weightSimilarity(a, b) {
|
|
|
1728
1760
|
normA += x * x;
|
|
1729
1761
|
normB += y * y;
|
|
1730
1762
|
}
|
|
1731
|
-
if (normA <=
|
|
1763
|
+
if (normA <= _chunkS5BKZZGMcjs.BEAT_EPS && normB <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1732
1764
|
return 1;
|
|
1733
1765
|
}
|
|
1734
|
-
if (normA <=
|
|
1766
|
+
if (normA <= _chunkS5BKZZGMcjs.BEAT_EPS || normB <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1735
1767
|
return 0;
|
|
1736
1768
|
}
|
|
1737
1769
|
return clamp01(dot / Math.sqrt(normA * normB));
|
|
@@ -1758,13 +1790,13 @@ function combineEvidence(strengths) {
|
|
|
1758
1790
|
// src/analyze/grid.ts
|
|
1759
1791
|
function gridOriginOf(firstOnset, slotBeats) {
|
|
1760
1792
|
const startBeat = firstOnset < 0 && firstOnset > -HUMANIZE_ADJACENCY ? 0 : firstOnset;
|
|
1761
|
-
return { origin: Math.floor(startBeat / slotBeats +
|
|
1793
|
+
return { origin: Math.floor(startBeat / slotBeats + _chunkS5BKZZGMcjs.BEAT_EPS) * slotBeats, startBeat };
|
|
1762
1794
|
}
|
|
1763
1795
|
function gridForNotes(notes, slotBeats) {
|
|
1764
1796
|
return gridOriginOf(firstSoundingBeat(notes), slotBeats);
|
|
1765
1797
|
}
|
|
1766
1798
|
function barGridStart(firstOnset, meter) {
|
|
1767
|
-
return gridOriginOf(firstOnset,
|
|
1799
|
+
return gridOriginOf(firstOnset, _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, firstOnset, meter)).startBeat;
|
|
1768
1800
|
}
|
|
1769
1801
|
|
|
1770
1802
|
// src/analyze/keys/index.ts
|
|
@@ -1784,13 +1816,13 @@ var FIFTHS_DISTANCE_COST = 0.18;
|
|
|
1784
1816
|
var KEY_CANDIDATES = (() => {
|
|
1785
1817
|
const candidates = [];
|
|
1786
1818
|
for (let rootPc = 0; rootPc < 12; rootPc += 1) {
|
|
1787
|
-
candidates.push({ key:
|
|
1788
|
-
candidates.push({ key:
|
|
1819
|
+
candidates.push({ key: _chunk3R3QVKY5cjs.majorKey.call(void 0, rootPc), rootPc, minor: false });
|
|
1820
|
+
candidates.push({ key: _chunk3R3QVKY5cjs.minorKey.call(void 0, rootPc), rootPc, minor: true });
|
|
1789
1821
|
}
|
|
1790
1822
|
return candidates;
|
|
1791
1823
|
})();
|
|
1792
1824
|
function fifthsOf(rootPc, minor) {
|
|
1793
|
-
const majorRoot =
|
|
1825
|
+
const majorRoot = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, minor ? rootPc + 3 : rootPc);
|
|
1794
1826
|
const raw = majorRoot * 7 % 12;
|
|
1795
1827
|
return raw > 6 ? raw - 12 : raw;
|
|
1796
1828
|
}
|
|
@@ -1808,9 +1840,9 @@ function chooseKeys(slots, changeCost, meter, budget) {
|
|
|
1808
1840
|
if (slots.length === 0) {
|
|
1809
1841
|
return [];
|
|
1810
1842
|
}
|
|
1811
|
-
const back =
|
|
1812
|
-
let costs =
|
|
1813
|
-
let spare =
|
|
1843
|
+
const back = _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slots.length, count, "key search choices", budget);
|
|
1844
|
+
let costs = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
|
|
1845
|
+
let spare = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, count, "key search costs", budget);
|
|
1814
1846
|
const opening = _nullishCoalesce(_optionalChain([slots, 'access', _11 => _11[0], 'optionalAccess', _12 => _12.scores]), () => ( []));
|
|
1815
1847
|
for (let c = 0; c < count; c += 1) {
|
|
1816
1848
|
costs[c] = -(_nullishCoalesce(opening[c], () => ( 0)));
|
|
@@ -1819,7 +1851,7 @@ function chooseKeys(slots, changeCost, meter, budget) {
|
|
|
1819
1851
|
const slot = slots[i];
|
|
1820
1852
|
const previous = costs;
|
|
1821
1853
|
const next = spare;
|
|
1822
|
-
const accent =
|
|
1854
|
+
const accent = _chunkS5BKZZGMcjs.metricWeight.call(void 0, _nullishCoalesce(_optionalChain([slot, 'optionalAccess', _13 => _13.startBeat]), () => ( 0)), meter);
|
|
1823
1855
|
const metricFactor = 1 - STRONG_BEAT_DISCOUNT * accent / MAX_METRIC_WEIGHT;
|
|
1824
1856
|
const rowBase = i * count;
|
|
1825
1857
|
for (let to = 0; to < count; to += 1) {
|
|
@@ -1889,7 +1921,7 @@ function enforceMinimumRegion(slots, path, minBeats, budget) {
|
|
|
1889
1921
|
return chosen;
|
|
1890
1922
|
}
|
|
1891
1923
|
const candidates = KEY_CANDIDATES.length;
|
|
1892
|
-
const totals =
|
|
1924
|
+
const totals = _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, slots.length, candidates, "key region scores", budget);
|
|
1893
1925
|
for (let i = 0; i < slots.length; i += 1) {
|
|
1894
1926
|
const scores = _nullishCoalesce(_optionalChain([slots, 'access', _18 => _18[i], 'optionalAccess', _19 => _19.scores]), () => ( []));
|
|
1895
1927
|
const base = i * candidates;
|
|
@@ -1920,13 +1952,13 @@ function enforceMinimumRegion(slots, path, minBeats, budget) {
|
|
|
1920
1952
|
return chosen;
|
|
1921
1953
|
}
|
|
1922
1954
|
const length = region.endBeat - region.startBeat;
|
|
1923
|
-
if (region.merged || length >= minBeats -
|
|
1955
|
+
if (region.merged || length >= minBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1924
1956
|
continue;
|
|
1925
1957
|
}
|
|
1926
1958
|
const previous = region.previous;
|
|
1927
1959
|
const next = region.next;
|
|
1928
|
-
const before = previous !== void 0 && Math.abs(previous.endBeat - region.startBeat) <
|
|
1929
|
-
const after = next !== void 0 && Math.abs(next.startBeat - region.endBeat) <
|
|
1960
|
+
const before = previous !== void 0 && Math.abs(previous.endBeat - region.startBeat) < _chunkS5BKZZGMcjs.BEAT_EPS ? previous : void 0;
|
|
1961
|
+
const after = next !== void 0 && Math.abs(next.startBeat - region.endBeat) < _chunkS5BKZZGMcjs.BEAT_EPS ? next : void 0;
|
|
1930
1962
|
if (before === void 0 && after === void 0) {
|
|
1931
1963
|
continue;
|
|
1932
1964
|
}
|
|
@@ -2036,7 +2068,7 @@ function withRelations(regions) {
|
|
|
2036
2068
|
if (previous === void 0 || current === void 0) {
|
|
2037
2069
|
continue;
|
|
2038
2070
|
}
|
|
2039
|
-
const relation =
|
|
2071
|
+
const relation = _chunk3R3QVKY5cjs.keyRelationBetween.call(void 0, previous.key, current.key);
|
|
2040
2072
|
if (relation !== null && relation !== "same") {
|
|
2041
2073
|
current.modulation = relation;
|
|
2042
2074
|
}
|
|
@@ -2044,22 +2076,22 @@ function withRelations(regions) {
|
|
|
2044
2076
|
return regions;
|
|
2045
2077
|
}
|
|
2046
2078
|
function keyTimelineFromNotes(notes, opts = {}) {
|
|
2047
|
-
const meters =
|
|
2048
|
-
const bar =
|
|
2079
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "key timeline meters");
|
|
2080
|
+
const bar = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters);
|
|
2049
2081
|
const expectedKeyBeats = _nullishCoalesce(opts.expectedKeyBeats, () => ( bar * 4));
|
|
2050
|
-
|
|
2082
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
|
|
2051
2083
|
const minKeyBeats = _nullishCoalesce(opts.minKeyBeats, () => ( bar));
|
|
2052
|
-
|
|
2084
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
|
|
2053
2085
|
const budget = opts.budget;
|
|
2054
|
-
|
|
2086
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "key timeline notes", { allowNonPositiveDuration: true, budget });
|
|
2055
2087
|
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
2056
2088
|
const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
2057
2089
|
const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastEnd));
|
|
2058
|
-
|
|
2090
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "key timeline totalBeats");
|
|
2059
2091
|
const slotBeats = Math.min(minKeyBeats, expectedKeyBeats);
|
|
2060
2092
|
const { origin, startBeat: musicStart } = gridForNotes(sounding, slotBeats);
|
|
2061
|
-
const slotCount = Math.max(0, Math.ceil((totalBeats - origin) / slotBeats -
|
|
2062
|
-
|
|
2093
|
+
const slotCount = Math.max(0, Math.ceil((totalBeats - origin) / slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS));
|
|
2094
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slotCount * KEY_CANDIDATES.length, "key timeline slots", budget);
|
|
2063
2095
|
if (slotCount === 0 || sounding.length === 0) {
|
|
2064
2096
|
return [];
|
|
2065
2097
|
}
|
|
@@ -2097,7 +2129,7 @@ function keyTimelineFromNotes(notes, opts = {}) {
|
|
|
2097
2129
|
(candidate) => profileScore(weights, candidate.minor ? profile.minor : profile.major, candidate.rootPc) * totalWeight
|
|
2098
2130
|
);
|
|
2099
2131
|
slots.push({ startBeat, endBeat, scores, weight: totalWeight });
|
|
2100
|
-
if (totalWeight >
|
|
2132
|
+
if (totalWeight > _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2101
2133
|
weightSum += totalWeight;
|
|
2102
2134
|
soundingSlots += 1;
|
|
2103
2135
|
}
|
|
@@ -2111,14 +2143,14 @@ function keyTimelineFromNotes(notes, opts = {}) {
|
|
|
2111
2143
|
const grouped = regionsFromPath(slots, path);
|
|
2112
2144
|
const head = grouped[0];
|
|
2113
2145
|
const next = grouped[1];
|
|
2114
|
-
if (head !== void 0 && next !== void 0 && head.endBeat - Math.max(head.startBeat, musicStart) < slotBeats -
|
|
2146
|
+
if (head !== void 0 && next !== void 0 && head.endBeat - Math.max(head.startBeat, musicStart) < slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2115
2147
|
next.startBeat = head.startBeat;
|
|
2116
2148
|
next.from = head.from;
|
|
2117
2149
|
grouped.shift();
|
|
2118
2150
|
}
|
|
2119
2151
|
const regions = grouped.map((region) => {
|
|
2120
2152
|
const candidate = KEY_CANDIDATES[region.candidate];
|
|
2121
|
-
const key =
|
|
2153
|
+
const key = _chunk3R3QVKY5cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _24 => _24.key]), () => ( _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
|
|
2122
2154
|
const startBeat = Math.max(region.startBeat, musicStart);
|
|
2123
2155
|
const weights = new Array(PITCH_CLASSES).fill(0);
|
|
2124
2156
|
for (let i = region.from; i < region.toExclusive; i += 1) {
|
|
@@ -2138,7 +2170,7 @@ function keyTimelineFromNotes(notes, opts = {}) {
|
|
|
2138
2170
|
return {
|
|
2139
2171
|
startBeat,
|
|
2140
2172
|
endBeat: region.endBeat,
|
|
2141
|
-
key:
|
|
2173
|
+
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, key),
|
|
2142
2174
|
confidence: Math.max(0, correlation)
|
|
2143
2175
|
};
|
|
2144
2176
|
});
|
|
@@ -2150,20 +2182,20 @@ var TONIC_CREDIT = 0.4;
|
|
|
2150
2182
|
var CADENCE_CREDIT = 0.6;
|
|
2151
2183
|
var CHORD_MODULATION_COST = 0.6;
|
|
2152
2184
|
function chordFitsKey(chord, candidate) {
|
|
2153
|
-
const tones =
|
|
2185
|
+
const tones = _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord);
|
|
2154
2186
|
if (tones.length === 0) {
|
|
2155
2187
|
return 0;
|
|
2156
2188
|
}
|
|
2157
2189
|
let inKey = 0;
|
|
2158
2190
|
for (const pc of tones) {
|
|
2159
|
-
if (
|
|
2191
|
+
if (_chunk3R3QVKY5cjs.isScaleTone.call(void 0, pc, candidate.key)) {
|
|
2160
2192
|
inKey += 1;
|
|
2161
2193
|
}
|
|
2162
2194
|
}
|
|
2163
2195
|
let score = CHORD_TONE_CREDIT * ((inKey - (tones.length - inKey)) / tones.length);
|
|
2164
|
-
const rootDegree =
|
|
2196
|
+
const rootDegree = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - candidate.rootPc);
|
|
2165
2197
|
if (rootDegree === 7) {
|
|
2166
|
-
if (
|
|
2198
|
+
if (_chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, chord)) {
|
|
2167
2199
|
score += DOMINANT_CREDIT;
|
|
2168
2200
|
} else if (chord.quality === "maj") {
|
|
2169
2201
|
score += DOMINANT_CREDIT * 0.5;
|
|
@@ -2175,17 +2207,17 @@ function chordFitsKey(chord, candidate) {
|
|
|
2175
2207
|
return score;
|
|
2176
2208
|
}
|
|
2177
2209
|
function detectModulations(chords, opts = {}) {
|
|
2178
|
-
|
|
2210
|
+
_chunkS5BKZZGMcjs.assertArray.call(void 0, chords, "modulation chord segments");
|
|
2179
2211
|
chords.forEach((segment, index) => {
|
|
2180
|
-
|
|
2212
|
+
_chunkS5BKZZGMcjs.assertRecord.call(void 0, segment, `modulation chord segments[${index}]`);
|
|
2181
2213
|
});
|
|
2182
|
-
const meters =
|
|
2183
|
-
const bar =
|
|
2214
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "modulation meters");
|
|
2215
|
+
const bar = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters);
|
|
2184
2216
|
const expectedKeyBeats = _nullishCoalesce(opts.expectedKeyBeats, () => ( bar * 4));
|
|
2185
|
-
|
|
2217
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, expectedKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "expectedKeyBeats");
|
|
2186
2218
|
const minKeyBeats = _nullishCoalesce(opts.minKeyBeats, () => ( bar));
|
|
2187
|
-
|
|
2188
|
-
|
|
2219
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, minKeyBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minKeyBeats");
|
|
2220
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
|
|
2189
2221
|
chords.length * KEY_CANDIDATES.length,
|
|
2190
2222
|
"modulation chord segments",
|
|
2191
2223
|
opts.budget
|
|
@@ -2193,7 +2225,7 @@ function detectModulations(chords, opts = {}) {
|
|
|
2193
2225
|
const sorted = [...chords].filter((segment) => segment.endBeat > segment.startBeat).sort((a, b) => a.startBeat - b.startBeat);
|
|
2194
2226
|
const lastChordEnd = sorted.reduce((end, segment) => Math.max(end, segment.endBeat), 0);
|
|
2195
2227
|
const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastChordEnd));
|
|
2196
|
-
|
|
2228
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "modulation totalBeats");
|
|
2197
2229
|
const ordered = sorted.map((segment) => ({ ...segment, endBeat: Math.min(segment.endBeat, totalBeats) })).filter((segment) => segment.endBeat > segment.startBeat);
|
|
2198
2230
|
if (ordered.length === 0) {
|
|
2199
2231
|
return [];
|
|
@@ -2216,14 +2248,14 @@ function detectModulations(chords, opts = {}) {
|
|
|
2216
2248
|
if (arriving === void 0 || approaching === void 0 || slot === void 0) {
|
|
2217
2249
|
continue;
|
|
2218
2250
|
}
|
|
2219
|
-
const isDominantMotion =
|
|
2251
|
+
const isDominantMotion = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, approaching.rootPc - arriving.rootPc) === 7 && _chunk3R3QVKY5cjs.soundsDominantSeventh.call(void 0, approaching);
|
|
2220
2252
|
if (!isDominantMotion) {
|
|
2221
2253
|
continue;
|
|
2222
2254
|
}
|
|
2223
2255
|
const length = slot.endBeat - slot.startBeat;
|
|
2224
2256
|
for (let c = 0; c < KEY_CANDIDATES.length; c += 1) {
|
|
2225
2257
|
const candidate = KEY_CANDIDATES[c];
|
|
2226
|
-
if (candidate !== void 0 &&
|
|
2258
|
+
if (candidate !== void 0 && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, arriving.rootPc - candidate.rootPc) === 0) {
|
|
2227
2259
|
slot.scores[c] = (_nullishCoalesce(slot.scores[c], () => ( 0))) + CADENCE_CREDIT * length;
|
|
2228
2260
|
}
|
|
2229
2261
|
}
|
|
@@ -2237,7 +2269,7 @@ function detectModulations(chords, opts = {}) {
|
|
|
2237
2269
|
const profile = resolveKeyProfile(opts.profile);
|
|
2238
2270
|
const regions = grouped.map((region) => {
|
|
2239
2271
|
const candidate = KEY_CANDIDATES[region.candidate];
|
|
2240
|
-
const key =
|
|
2272
|
+
const key = _chunk3R3QVKY5cjs.resolveKey.call(void 0, _nullishCoalesce(_optionalChain([candidate, 'optionalAccess', _31 => _31.key]), () => ( _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
|
|
2241
2273
|
const weights = new Array(PITCH_CLASSES).fill(0);
|
|
2242
2274
|
for (let i = region.from; i < region.toExclusive; i += 1) {
|
|
2243
2275
|
const segment = ordered[i];
|
|
@@ -2245,10 +2277,10 @@ function detectModulations(chords, opts = {}) {
|
|
|
2245
2277
|
continue;
|
|
2246
2278
|
}
|
|
2247
2279
|
const overlap = segment.endBeat - segment.startBeat;
|
|
2248
|
-
if (overlap <=
|
|
2280
|
+
if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2249
2281
|
continue;
|
|
2250
2282
|
}
|
|
2251
|
-
for (const pc of
|
|
2283
|
+
for (const pc of _chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, segment.chord)) {
|
|
2252
2284
|
weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + overlap;
|
|
2253
2285
|
}
|
|
2254
2286
|
}
|
|
@@ -2273,7 +2305,7 @@ function detectModulations(chords, opts = {}) {
|
|
|
2273
2305
|
var THIRD_SEMITONES = [3, 4];
|
|
2274
2306
|
var FIFTH_SEMITONES = [6, 7, 8];
|
|
2275
2307
|
function triadCore(chord) {
|
|
2276
|
-
const root =
|
|
2308
|
+
const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
2277
2309
|
let third;
|
|
2278
2310
|
let fifth;
|
|
2279
2311
|
for (const interval of chord.intervals) {
|
|
@@ -2289,7 +2321,7 @@ function triadCore(chord) {
|
|
|
2289
2321
|
if (third === void 0 || fifth === void 0) {
|
|
2290
2322
|
return null;
|
|
2291
2323
|
}
|
|
2292
|
-
return [root,
|
|
2324
|
+
return [root, _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, root + third), _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, root + fifth)].sort((a, b) => a - b);
|
|
2293
2325
|
}
|
|
2294
2326
|
function lastChordFinishedBy(chords) {
|
|
2295
2327
|
const byEnd = chords.map((_, index) => index).sort((a, b) => (_nullishCoalesce(_optionalChain([chords, 'access', _34 => _34[a], 'optionalAccess', _35 => _35.endBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([chords, 'access', _36 => _36[b], 'optionalAccess', _37 => _37.endBeat]), () => ( 0))));
|
|
@@ -2327,7 +2359,7 @@ function attachPivots(regions, chords) {
|
|
|
2327
2359
|
if (previous === void 0 || current === void 0) {
|
|
2328
2360
|
continue;
|
|
2329
2361
|
}
|
|
2330
|
-
const last = finished(current.startBeat +
|
|
2362
|
+
const last = finished(current.startBeat + _chunkS5BKZZGMcjs.BEAT_EPS);
|
|
2331
2363
|
if (last === void 0) {
|
|
2332
2364
|
continue;
|
|
2333
2365
|
}
|
|
@@ -2335,9 +2367,9 @@ function attachPivots(regions, chords) {
|
|
|
2335
2367
|
if (core === null) {
|
|
2336
2368
|
continue;
|
|
2337
2369
|
}
|
|
2338
|
-
const root =
|
|
2370
|
+
const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, last.chord.rootPc);
|
|
2339
2371
|
const pivot = pivotChords(previous.key, current.key).find(
|
|
2340
|
-
(entry) => entry.chord.rootPc === root && sameTones(
|
|
2372
|
+
(entry) => entry.chord.rootPc === root && sameTones(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, entry.chord), core)
|
|
2341
2373
|
);
|
|
2342
2374
|
if (pivot !== void 0) {
|
|
2343
2375
|
current.pivot = pivot;
|
|
@@ -2362,12 +2394,12 @@ function keyLookup(regions, fallback) {
|
|
|
2362
2394
|
};
|
|
2363
2395
|
}
|
|
2364
2396
|
function prevailingKeyOf(regions) {
|
|
2365
|
-
|
|
2397
|
+
_chunkS5BKZZGMcjs.assertArray.call(void 0, regions, "key regions");
|
|
2366
2398
|
const totals = /* @__PURE__ */ new Map();
|
|
2367
2399
|
for (const [index, given] of regions.entries()) {
|
|
2368
|
-
const region =
|
|
2369
|
-
|
|
2370
|
-
const id = `${
|
|
2400
|
+
const region = _chunkS5BKZZGMcjs.assertRecord.call(void 0, given, `key regions[${index}]`);
|
|
2401
|
+
_chunkS5BKZZGMcjs.assertRecord.call(void 0, region.key, `key regions[${index}].key`);
|
|
2402
|
+
const id = `${_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, region.key.scale.rootPc)}:${region.key.scale.modeMask12}`;
|
|
2371
2403
|
const entry = totals.get(id);
|
|
2372
2404
|
const length = Math.max(0, region.endBeat - region.startBeat);
|
|
2373
2405
|
if (entry === void 0) {
|
|
@@ -2392,11 +2424,11 @@ function keyScaleAt(key) {
|
|
|
2392
2424
|
if (typeof key === "function") {
|
|
2393
2425
|
return key;
|
|
2394
2426
|
}
|
|
2395
|
-
const scale =
|
|
2427
|
+
const scale = _chunk3R3QVKY5cjs.toKeyScale.call(void 0, key);
|
|
2396
2428
|
return () => scale;
|
|
2397
2429
|
}
|
|
2398
2430
|
function toVoiceNotes(events) {
|
|
2399
|
-
|
|
2431
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, "voice notes", { allowNonPositiveDuration: true });
|
|
2400
2432
|
return events.map((event, index) => ({ ...event, id: index, originalIndex: index }));
|
|
2401
2433
|
}
|
|
2402
2434
|
function intervalAboveBass(pitch, others, chord) {
|
|
@@ -2408,7 +2440,7 @@ function intervalAboveBass(pitch, others, chord) {
|
|
|
2408
2440
|
return ((pitch - bass) % 12 + 12) % 12;
|
|
2409
2441
|
}
|
|
2410
2442
|
const ref = _nullishCoalesce(chord.bassPc, () => ( chord.rootPc));
|
|
2411
|
-
return ((
|
|
2443
|
+
return ((_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pitch) - _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, ref)) % 12 + 12) % 12;
|
|
2412
2444
|
}
|
|
2413
2445
|
var TENSION_DEGREE_BY_INTERVAL = {
|
|
2414
2446
|
1: 9,
|
|
@@ -2427,7 +2459,7 @@ function asSounded(chord) {
|
|
|
2427
2459
|
if (chord.bassPc === void 0) {
|
|
2428
2460
|
return chord;
|
|
2429
2461
|
}
|
|
2430
|
-
return { ...chord, intervals: [...chord.intervals,
|
|
2462
|
+
return { ...chord, intervals: [...chord.intervals, _chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, chord.bassPc, chord)] };
|
|
2431
2463
|
}
|
|
2432
2464
|
function isStep(a, b) {
|
|
2433
2465
|
const d = Math.abs(a - b);
|
|
@@ -2456,19 +2488,19 @@ var STEP_RESOLUTION = 2;
|
|
|
2456
2488
|
var NEAREST_CHORD_TONE_REACH = 12;
|
|
2457
2489
|
function nearestChordTone(pitch, chord, maxDistance) {
|
|
2458
2490
|
for (let delta = 1; delta <= maxDistance; delta += 1) {
|
|
2459
|
-
if (
|
|
2491
|
+
if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, pitch - delta, chord)) {
|
|
2460
2492
|
return pitch - delta;
|
|
2461
2493
|
}
|
|
2462
|
-
if (
|
|
2494
|
+
if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, pitch + delta, chord)) {
|
|
2463
2495
|
return pitch + delta;
|
|
2464
2496
|
}
|
|
2465
2497
|
}
|
|
2466
2498
|
return void 0;
|
|
2467
2499
|
}
|
|
2468
2500
|
function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2501
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, voice, "voice notes", { allowNonPositiveDuration: true });
|
|
2502
|
+
_chunkS5BKZZGMcjs.assertFunction.call(void 0, chordAtBeat2, "chordAtBeat");
|
|
2503
|
+
_chunkS5BKZZGMcjs.assertFunction.call(void 0, otherVoicesAtBeat, "otherVoicesAtBeat");
|
|
2472
2504
|
const keyAt = keyScaleAt(key);
|
|
2473
2505
|
const result = [];
|
|
2474
2506
|
for (let i = 0; i < voice.length; i += 1) {
|
|
@@ -2484,11 +2516,11 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2484
2516
|
const next = nextNote !== void 0 && adjacent(noteEnd, nextNote.startBeat) ? nextNote : void 0;
|
|
2485
2517
|
const chord = chordAtBeat2(note.startBeat);
|
|
2486
2518
|
const labels = [];
|
|
2487
|
-
const member =
|
|
2519
|
+
const member = _chunk3R3QVKY5cjs.isChordMember.call(void 0, note.pitch, chord);
|
|
2488
2520
|
let handled = false;
|
|
2489
2521
|
if (chord && member) {
|
|
2490
|
-
const role =
|
|
2491
|
-
const degree = role === null ? tensionDegree(
|
|
2522
|
+
const role = _chunk3R3QVKY5cjs.chordToneRole.call(void 0, note.pitch, chord);
|
|
2523
|
+
const degree = role === null ? tensionDegree(_chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, note.pitch, chord)) : void 0;
|
|
2492
2524
|
if (role) {
|
|
2493
2525
|
labels.push({ kind: "chordTone", role });
|
|
2494
2526
|
handled = true;
|
|
@@ -2496,7 +2528,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2496
2528
|
labels.push({ kind: "tension", degree });
|
|
2497
2529
|
handled = true;
|
|
2498
2530
|
} else {
|
|
2499
|
-
const bassRole =
|
|
2531
|
+
const bassRole = _chunk3R3QVKY5cjs.chordToneRole.call(void 0, note.pitch, asSounded(chord));
|
|
2500
2532
|
if (bassRole !== null) {
|
|
2501
2533
|
labels.push({ kind: "chordTone", role: bassRole });
|
|
2502
2534
|
handled = true;
|
|
@@ -2505,11 +2537,11 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2505
2537
|
}
|
|
2506
2538
|
if (!handled && chord && prev && next) {
|
|
2507
2539
|
const prevChord = chordAtBeat2(prev.startBeat);
|
|
2508
|
-
const prepared = prev.pitch === note.pitch &&
|
|
2540
|
+
const prepared = prev.pitch === note.pitch && _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, prevChord);
|
|
2509
2541
|
const resolves = isStep(next.pitch, note.pitch);
|
|
2510
2542
|
const others = otherVoicesAtBeat(note.startBeat);
|
|
2511
2543
|
const verticallyDissonant = others.some(
|
|
2512
|
-
(ov) =>
|
|
2544
|
+
(ov) => _chunk3R3QVKY5cjs.createsVerticalDissonance.call(void 0, note.pitch, ov.pitch, true)
|
|
2513
2545
|
);
|
|
2514
2546
|
if (prepared && verticallyDissonant && resolves) {
|
|
2515
2547
|
const ic = intervalAboveBass(note.pitch, others, chord);
|
|
@@ -2521,8 +2553,8 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2521
2553
|
}
|
|
2522
2554
|
}
|
|
2523
2555
|
if (!handled && chord && prev && next) {
|
|
2524
|
-
const prevMember =
|
|
2525
|
-
const nextMember =
|
|
2556
|
+
const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
|
|
2557
|
+
const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
|
|
2526
2558
|
const up = note.pitch - prev.pitch > 0;
|
|
2527
2559
|
const contInto = next.pitch - note.pitch > 0;
|
|
2528
2560
|
const sameDir = note.pitch !== prev.pitch && next.pitch !== note.pitch && up === contInto;
|
|
@@ -2531,20 +2563,20 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2531
2563
|
handled = true;
|
|
2532
2564
|
}
|
|
2533
2565
|
}
|
|
2534
|
-
if (!handled && chord && prev && next && !member && prev.pitch === next.pitch &&
|
|
2566
|
+
if (!handled && chord && prev && next && !member && prev.pitch === next.pitch && _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat)) && isStep(note.pitch, prev.pitch)) {
|
|
2535
2567
|
labels.push({ kind: "neighbor" });
|
|
2536
2568
|
handled = true;
|
|
2537
2569
|
}
|
|
2538
2570
|
if (!handled && chord && next && !member && note.pitch === next.pitch) {
|
|
2539
2571
|
const nextChord = chordAtBeat2(next.startBeat);
|
|
2540
|
-
if (
|
|
2572
|
+
if (_chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, nextChord)) {
|
|
2541
2573
|
labels.push({ kind: "anticipation" });
|
|
2542
2574
|
handled = true;
|
|
2543
2575
|
}
|
|
2544
2576
|
}
|
|
2545
2577
|
if (!handled && chord && prev && next && !member) {
|
|
2546
|
-
const prevMember =
|
|
2547
|
-
const nextMember =
|
|
2578
|
+
const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
|
|
2579
|
+
const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
|
|
2548
2580
|
const leapFromPrev = Math.abs(note.pitch - prev.pitch) >= LEAP_SEMITONES;
|
|
2549
2581
|
const stepToNext = isStep(next.pitch, note.pitch);
|
|
2550
2582
|
const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
|
|
@@ -2554,8 +2586,8 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2554
2586
|
}
|
|
2555
2587
|
}
|
|
2556
2588
|
if (!handled && chord && prev && next && !member) {
|
|
2557
|
-
const prevMember =
|
|
2558
|
-
const nextMember =
|
|
2589
|
+
const prevMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat2(prev.startBeat));
|
|
2590
|
+
const nextMember = _chunk3R3QVKY5cjs.isChordMember.call(void 0, next.pitch, chordAtBeat2(next.startBeat));
|
|
2559
2591
|
const stepFromPrev = isStep(note.pitch, prev.pitch);
|
|
2560
2592
|
const leapToNext = Math.abs(next.pitch - note.pitch) >= LEAP_SEMITONES;
|
|
2561
2593
|
const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
|
|
@@ -2565,7 +2597,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2565
2597
|
}
|
|
2566
2598
|
}
|
|
2567
2599
|
if (!handled && chord && !member) {
|
|
2568
|
-
const ic =
|
|
2600
|
+
const ic = _chunk3R3QVKY5cjs.intervalAboveRoot.call(void 0, note.pitch, chord);
|
|
2569
2601
|
const degree = tensionDegree(ic);
|
|
2570
2602
|
const colours = NATURAL_TENSION_INTERVALS.has(ic) || chord.intervals.some((i2) => i2 === 10 || i2 === 11);
|
|
2571
2603
|
const avoid = ic === 5 && chord.intervals.includes(4) && !chord.intervals.includes(5) || ic === 11 && chord.intervals.includes(4) && chord.intervals.includes(10);
|
|
@@ -2584,7 +2616,7 @@ function analyzeVoice(voice, chordAtBeat2, key, otherVoicesAtBeat = () => []) {
|
|
|
2584
2616
|
);
|
|
2585
2617
|
}
|
|
2586
2618
|
}
|
|
2587
|
-
if (next &&
|
|
2619
|
+
if (next && _chunk3R3QVKY5cjs.isLeadingToneResolution.call(void 0, note.pitch, next.pitch, keyAt(next.startBeat))) {
|
|
2588
2620
|
labels.push({ kind: "leadingTone", resolveTo: next.pitch });
|
|
2589
2621
|
}
|
|
2590
2622
|
const analyzed = {
|
|
@@ -2635,22 +2667,22 @@ function describe(labels) {
|
|
|
2635
2667
|
|
|
2636
2668
|
// src/analyze/timeline/index.ts
|
|
2637
2669
|
function assertChordTimeline(timeline, name = "timeline") {
|
|
2638
|
-
const read =
|
|
2639
|
-
|
|
2640
|
-
const segments =
|
|
2670
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, timeline, name);
|
|
2671
|
+
_chunkS5BKZZGMcjs.assertFunction.call(void 0, read.at, `${name}.at`);
|
|
2672
|
+
const segments = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.segments, `${name}.segments`);
|
|
2641
2673
|
for (let index = 0; index < segments.length; index += 1) {
|
|
2642
|
-
const segment =
|
|
2643
|
-
|
|
2644
|
-
|
|
2674
|
+
const segment = _chunkS5BKZZGMcjs.assertRecord.call(void 0, segments[index], `${name}.segments[${index}]`);
|
|
2675
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, segment.startBeat, `${name}.segments[${index}].startBeat`);
|
|
2676
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, segment.endBeat, `${name}.segments[${index}].endBeat`);
|
|
2645
2677
|
}
|
|
2646
2678
|
return read;
|
|
2647
2679
|
}
|
|
2648
2680
|
function chordTimelineFromChords(chords, totalBeats) {
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2681
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
|
|
2682
|
+
_chunkS5BKZZGMcjs.assertArray.call(void 0, chords, "chords");
|
|
2683
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, chords.length, "timeline chords");
|
|
2652
2684
|
for (let index = 0; index < chords.length; index += 1) {
|
|
2653
|
-
|
|
2685
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0,
|
|
2654
2686
|
_nullishCoalesce(_optionalChain([chords, 'access', _42 => _42[index], 'optionalAccess', _43 => _43.startBeat]), () => ( Number.NaN)),
|
|
2655
2687
|
-Number.MAX_SAFE_INTEGER,
|
|
2656
2688
|
Number.MAX_SAFE_INTEGER,
|
|
@@ -2665,13 +2697,13 @@ function chordTimelineFromChords(chords, totalBeats) {
|
|
|
2665
2697
|
return {
|
|
2666
2698
|
startBeat: gc.startBeat,
|
|
2667
2699
|
endBeat,
|
|
2668
|
-
chord:
|
|
2700
|
+
chord: _chunk3R3QVKY5cjs.chordFromSpan.call(void 0, gc)
|
|
2669
2701
|
};
|
|
2670
2702
|
}).filter((seg) => seg.endBeat > seg.startBeat);
|
|
2671
2703
|
return { at: segmentLookup(segments), segments };
|
|
2672
2704
|
}
|
|
2673
2705
|
function chordAtBeat(segments, beat) {
|
|
2674
|
-
|
|
2706
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "timeline query beat");
|
|
2675
2707
|
const candidate = segments[firstAfter(segments, beat) - 1];
|
|
2676
2708
|
return candidate !== void 0 && beat >= candidate.startBeat && beat < candidate.endBeat ? candidate.chord : null;
|
|
2677
2709
|
}
|
|
@@ -2691,7 +2723,7 @@ function firstAfter(segments, beat) {
|
|
|
2691
2723
|
function segmentStartingAt(segments, beat) {
|
|
2692
2724
|
const index = firstAfter(segments, beat);
|
|
2693
2725
|
for (const candidate of [segments[index - 1], segments[index]]) {
|
|
2694
|
-
if (candidate !== void 0 && Math.abs(candidate.startBeat - beat) <
|
|
2726
|
+
if (candidate !== void 0 && Math.abs(candidate.startBeat - beat) < _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2695
2727
|
return candidate;
|
|
2696
2728
|
}
|
|
2697
2729
|
}
|
|
@@ -2712,10 +2744,10 @@ var OCTAVE = 12;
|
|
|
2712
2744
|
function scoreMatch(match, weights, maxWeight, key, pedalPc) {
|
|
2713
2745
|
const rootWeight = maxWeight > 0 ? (_nullishCoalesce(weights[match.rootPc], () => ( 0))) / maxWeight : 0;
|
|
2714
2746
|
const rootStanding = match.inversion === 0 ? Math.max(rootWeight, BASS_ROOT_STANDING) : rootWeight;
|
|
2715
|
-
const chord =
|
|
2747
|
+
const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, match.rootPc, match.quality);
|
|
2716
2748
|
const account = matchAccount(match, chord, pedalPc);
|
|
2717
2749
|
let score = rootStanding;
|
|
2718
|
-
if (
|
|
2750
|
+
if (_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunk3R3QVKY5cjs.isScaleTone.call(void 0, pc, key))) {
|
|
2719
2751
|
score += DIATONIC_BONUS;
|
|
2720
2752
|
}
|
|
2721
2753
|
if (account.exact) {
|
|
@@ -2730,7 +2762,7 @@ function isSlashReading(match, pedalPc) {
|
|
|
2730
2762
|
return pedalPc !== null && match.inversion === null && match.missingPcs.length === 0 && match.extraPcs.length === 1 && match.extraPcs[0] === pedalPc && SLASH_UPPER_QUALITIES.has(match.quality);
|
|
2731
2763
|
}
|
|
2732
2764
|
function namesAsExtension(chord, pc) {
|
|
2733
|
-
return chord.intervals.some((iv) => iv >= OCTAVE &&
|
|
2765
|
+
return chord.intervals.some((iv) => iv >= OCTAVE && _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === pc);
|
|
2734
2766
|
}
|
|
2735
2767
|
function matchAccount(match, chord, pedalPc) {
|
|
2736
2768
|
const unvoiced = pedalPc !== null && namesAsExtension(chord, pedalPc) ? 1 : 0;
|
|
@@ -2746,10 +2778,10 @@ function bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) {
|
|
|
2746
2778
|
let highestBass = Number.NEGATIVE_INFINITY;
|
|
2747
2779
|
for (const note of notes) {
|
|
2748
2780
|
const overlap = Math.min(note.startBeat + note.durationBeat, windowEnd) - Math.max(note.startBeat, windowStart);
|
|
2749
|
-
if (overlap <=
|
|
2781
|
+
if (overlap <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2750
2782
|
continue;
|
|
2751
2783
|
}
|
|
2752
|
-
if (
|
|
2784
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, note.pitch) === bassPc) {
|
|
2753
2785
|
highestBass = Math.max(highestBass, note.pitch);
|
|
2754
2786
|
} else {
|
|
2755
2787
|
upperFloor = Math.min(upperFloor, note.pitch);
|
|
@@ -2758,7 +2790,7 @@ function bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) {
|
|
|
2758
2790
|
return highestBass <= upperFloor;
|
|
2759
2791
|
}
|
|
2760
2792
|
function chordConfidence(chord, weights, totalWeight, exact) {
|
|
2761
|
-
const tones = new Set(
|
|
2793
|
+
const tones = new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord));
|
|
2762
2794
|
let chordWeight = 0;
|
|
2763
2795
|
for (let pc = 0; pc < 12; pc += 1) {
|
|
2764
2796
|
if (tones.has(pc)) {
|
|
@@ -2776,12 +2808,12 @@ function analyzeWindow(notes, windowStart, windowEnd, meters, key) {
|
|
|
2776
2808
|
windowEnd,
|
|
2777
2809
|
meters
|
|
2778
2810
|
);
|
|
2779
|
-
if (totalWeight <=
|
|
2811
|
+
if (totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2780
2812
|
return null;
|
|
2781
2813
|
}
|
|
2782
2814
|
const ranked = weights.map((weight, pc) => ({ pc, weight })).filter(({ weight }) => weight > 0).sort((a, b) => b.weight - a.weight || a.pc - b.pc);
|
|
2783
2815
|
const selected = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
|
|
2784
|
-
const bassPc =
|
|
2816
|
+
const bassPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, lowestPitch);
|
|
2785
2817
|
const hasBass = selected.includes(bassPc);
|
|
2786
2818
|
const detectionPitches = hasBass ? selected.map((pc) => pc === bassPc ? pc : pc + 12) : selected;
|
|
2787
2819
|
const pedalPc = hasBass && bassSoundsOnlyBelow(notes, windowStart, windowEnd, bassPc) ? bassPc : null;
|
|
@@ -2789,17 +2821,17 @@ function analyzeWindow(notes, windowStart, windowEnd, meters, key) {
|
|
|
2789
2821
|
let bestMatch = null;
|
|
2790
2822
|
let bestScore = Number.NEGATIVE_INFINITY;
|
|
2791
2823
|
for (const match of matches) {
|
|
2792
|
-
const score = scoreMatch(match, weights, maxWeight,
|
|
2824
|
+
const score = scoreMatch(match, weights, maxWeight, _chunk3R3QVKY5cjs.scaleOf.call(void 0, key), pedalPc);
|
|
2793
2825
|
if (score > bestScore) {
|
|
2794
2826
|
bestScore = score;
|
|
2795
2827
|
bestMatch = match;
|
|
2796
2828
|
}
|
|
2797
2829
|
}
|
|
2798
2830
|
if (bestMatch) {
|
|
2799
|
-
const chord =
|
|
2831
|
+
const chord = _chunk3R3QVKY5cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality, bestMatch.bassPc);
|
|
2800
2832
|
const account = matchAccount(
|
|
2801
2833
|
bestMatch,
|
|
2802
|
-
|
|
2834
|
+
_chunk3R3QVKY5cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality),
|
|
2803
2835
|
pedalPc
|
|
2804
2836
|
);
|
|
2805
2837
|
const reading = {
|
|
@@ -2823,7 +2855,7 @@ function shared(chord) {
|
|
|
2823
2855
|
}
|
|
2824
2856
|
var DOMINANT_ABOVE_TONIC = 7;
|
|
2825
2857
|
function resolvesOntoDominant(readings, index, key) {
|
|
2826
|
-
const dominantPc =
|
|
2858
|
+
const dominantPc = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc + DOMINANT_ABOVE_TONIC);
|
|
2827
2859
|
const candidate = _optionalChain([readings, 'access', _46 => _46[index], 'optionalAccess', _47 => _47.chord]);
|
|
2828
2860
|
for (let i = index + 1; i < readings.length; i += 1) {
|
|
2829
2861
|
const next = readings[i];
|
|
@@ -2834,10 +2866,10 @@ function resolvesOntoDominant(readings, index, key) {
|
|
|
2834
2866
|
if (candidate !== void 0 && sameChord(chord, candidate)) {
|
|
2835
2867
|
continue;
|
|
2836
2868
|
}
|
|
2837
|
-
if (
|
|
2869
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc))) !== dominantPc) {
|
|
2838
2870
|
return false;
|
|
2839
2871
|
}
|
|
2840
|
-
if (
|
|
2872
|
+
if (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc) === dominantPc) {
|
|
2841
2873
|
return true;
|
|
2842
2874
|
}
|
|
2843
2875
|
}
|
|
@@ -2870,7 +2902,7 @@ var LEXICON_TONES = (() => {
|
|
|
2870
2902
|
for (const quality of SEGMENTATION_QUALITIES) {
|
|
2871
2903
|
LEXICON_ROOTS[candidate] = rootPc;
|
|
2872
2904
|
LEXICON_TONE_START[candidate] = tones.length;
|
|
2873
|
-
tones.push(...
|
|
2905
|
+
tones.push(..._chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, _chunk3R3QVKY5cjs.makeChord.call(void 0, rootPc, quality)));
|
|
2874
2906
|
candidate += 1;
|
|
2875
2907
|
}
|
|
2876
2908
|
}
|
|
@@ -2891,7 +2923,7 @@ function candidateScore(candidate, weights, totalWeight) {
|
|
|
2891
2923
|
for (let index = first; index < last; index += 1) {
|
|
2892
2924
|
const weight = _nullishCoalesce(weights[_nullishCoalesce(LEXICON_TONES[index], () => ( 0))], () => ( 0));
|
|
2893
2925
|
covered += weight;
|
|
2894
|
-
if (weight <=
|
|
2926
|
+
if (weight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2895
2927
|
missing += 1;
|
|
2896
2928
|
}
|
|
2897
2929
|
}
|
|
@@ -2907,16 +2939,16 @@ function slotOfBeat(grid, beat) {
|
|
|
2907
2939
|
}
|
|
2908
2940
|
function createBoundaryTables(slotCount, budget) {
|
|
2909
2941
|
return {
|
|
2910
|
-
scores:
|
|
2911
|
-
back:
|
|
2912
|
-
costs:
|
|
2913
|
-
spare:
|
|
2914
|
-
path:
|
|
2942
|
+
scores: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window scores", budget),
|
|
2943
|
+
back: _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slotCount, LEXICON_SIZE, "timeline window choices", budget),
|
|
2944
|
+
costs: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
|
|
2945
|
+
spare: _chunkS5BKZZGMcjs.allocateCandidateTable.call(void 0, 1, LEXICON_SIZE, "timeline window costs", budget),
|
|
2946
|
+
path: _chunkS5BKZZGMcjs.allocateChoiceTable.call(void 0, slotCount, 1, "timeline window path", budget)
|
|
2915
2947
|
};
|
|
2916
2948
|
}
|
|
2917
2949
|
function scoreSlot(tables, slot, index) {
|
|
2918
2950
|
const base = index * LEXICON_SIZE;
|
|
2919
|
-
if (slot.totalWeight <=
|
|
2951
|
+
if (slot.totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
2920
2952
|
tables.scores.fill(0, base, base + LEXICON_SIZE);
|
|
2921
2953
|
return;
|
|
2922
2954
|
}
|
|
@@ -2946,7 +2978,7 @@ function chooseBoundaries(tables, from, to, grid, meters, changeCost) {
|
|
|
2946
2978
|
}
|
|
2947
2979
|
}
|
|
2948
2980
|
const slotStart = beatOfSlot(grid, from + i);
|
|
2949
|
-
const accent =
|
|
2981
|
+
const accent = _chunkS5BKZZGMcjs.metricWeight.call(void 0, slotStart, meters);
|
|
2950
2982
|
const penalty = changeCost * (1 - STRONG_BEAT_DISCOUNT2 * accent / MAX_METRIC_WEIGHT2);
|
|
2951
2983
|
const rowBase = (from + i) * LEXICON_SIZE;
|
|
2952
2984
|
const change = cheapest + penalty;
|
|
@@ -2988,7 +3020,7 @@ function chooseBoundaries(tables, from, to, grid, meters, changeCost) {
|
|
|
2988
3020
|
}
|
|
2989
3021
|
function slotBeatsWithin(gridUnit, minChordBeats, harmonicRhythm) {
|
|
2990
3022
|
const resolution = Math.min(minChordBeats, harmonicRhythm);
|
|
2991
|
-
const divisions = Math.max(1, Math.ceil(gridUnit / resolution -
|
|
3023
|
+
const divisions = Math.max(1, Math.ceil(gridUnit / resolution - _chunkS5BKZZGMcjs.BEAT_EPS));
|
|
2992
3024
|
return gridUnit / divisions;
|
|
2993
3025
|
}
|
|
2994
3026
|
function gridSpans(slotCount, grid) {
|
|
@@ -3004,7 +3036,7 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
|
|
|
3004
3036
|
let weightSum = 0;
|
|
3005
3037
|
for (let i = 0; i < slotCount; i += 1) {
|
|
3006
3038
|
const weight = _nullishCoalesce(_optionalChain([slotWeights, 'access', _48 => _48[i], 'optionalAccess', _49 => _49.totalWeight]), () => ( 0));
|
|
3007
|
-
if (weight >
|
|
3039
|
+
if (weight > _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3008
3040
|
soundingSlots += 1;
|
|
3009
3041
|
weightSum += weight;
|
|
3010
3042
|
}
|
|
@@ -3014,7 +3046,7 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
|
|
|
3014
3046
|
}
|
|
3015
3047
|
const meanSlotWeight = weightSum / soundingSlots;
|
|
3016
3048
|
const changeCost = CHANGE_COST * meanSlotWeight * (harmonicRhythm / slotBeats);
|
|
3017
|
-
const silent = slotWeights.map((slot) => slot.totalWeight <=
|
|
3049
|
+
const silent = slotWeights.map((slot) => slot.totalWeight <= _chunkS5BKZZGMcjs.BEAT_EPS);
|
|
3018
3050
|
const breaks = new Array(slotCount).fill(false);
|
|
3019
3051
|
for (let i = 0; i < slotCount; ) {
|
|
3020
3052
|
if (!silent[i]) {
|
|
@@ -3025,7 +3057,7 @@ function dynamicSpans(slotWeights, tables, grid, harmonicRhythm, meters) {
|
|
|
3025
3057
|
while (end < slotCount && silent[end]) {
|
|
3026
3058
|
end += 1;
|
|
3027
3059
|
}
|
|
3028
|
-
if ((end - i) * slotBeats >= harmonicRhythm -
|
|
3060
|
+
if ((end - i) * slotBeats >= harmonicRhythm - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3029
3061
|
for (let k = i; k < end; k += 1) {
|
|
3030
3062
|
breaks[k] = true;
|
|
3031
3063
|
}
|
|
@@ -3088,30 +3120,30 @@ function evidenceFits(previous, grid, slotCount, totalBeats, segmentation, harmo
|
|
|
3088
3120
|
return previous.grid.origin === grid.origin && previous.grid.slotBeats === grid.slotBeats && previous.slotCount === slotCount && previous.totalBeats === totalBeats && previous.segmentation === segmentation && previous.harmonicRhythm === harmonicRhythm && previous.keyGiven === keyGiven;
|
|
3089
3121
|
}
|
|
3090
3122
|
function analyzeTimeline(notes, opts = {}, previous, dirty) {
|
|
3091
|
-
const meters =
|
|
3092
|
-
const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => (
|
|
3093
|
-
|
|
3123
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "timeline meters");
|
|
3124
|
+
const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => ( _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters)));
|
|
3125
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
|
|
3094
3126
|
const segmentation = _nullishCoalesce(opts.segmentation, () => ( "dynamic"));
|
|
3095
|
-
const gridUnit =
|
|
3127
|
+
const gridUnit = _chunkS5BKZZGMcjs.metricGridUnit.call(void 0, meters);
|
|
3096
3128
|
const minChordBeats = _nullishCoalesce(opts.minChordBeats, () => ( gridUnit));
|
|
3097
|
-
|
|
3129
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, minChordBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "minChordBeats");
|
|
3098
3130
|
const budget = opts.budget;
|
|
3099
3131
|
const noteOptions = { allowNonPositiveDuration: true, budget };
|
|
3100
3132
|
if (opts.pickupBeats !== void 0) {
|
|
3101
|
-
|
|
3133
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "timeline pickupBeats");
|
|
3102
3134
|
noteOptions.minStartBeat = -opts.pickupBeats;
|
|
3103
3135
|
}
|
|
3104
|
-
|
|
3105
|
-
const sounding =
|
|
3136
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "timeline notes", noteOptions);
|
|
3137
|
+
const sounding = _chunk35NBV2FTcjs.sortedNoteEvents.call(void 0, notes.filter((note) => note.durationBeat > 0));
|
|
3106
3138
|
const lastNoteEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
3107
3139
|
const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastNoteEnd));
|
|
3108
|
-
|
|
3140
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
|
|
3109
3141
|
const slotBeats = segmentation === "grid" ? harmonicRhythm : slotBeatsWithin(gridUnit, minChordBeats, harmonicRhythm);
|
|
3110
3142
|
const { origin, startBeat: musicStart } = gridForNotes(sounding, slotBeats);
|
|
3111
3143
|
const grid = { origin, slotBeats };
|
|
3112
|
-
const slotCount = Math.max(0, Math.ceil((totalBeats - grid.origin) / slotBeats -
|
|
3144
|
+
const slotCount = Math.max(0, Math.ceil((totalBeats - grid.origin) / slotBeats - _chunkS5BKZZGMcjs.BEAT_EPS));
|
|
3113
3145
|
const dynamic = segmentation === "dynamic";
|
|
3114
|
-
|
|
3146
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
|
|
3115
3147
|
dynamic ? slotCount * LEXICON_SIZE : slotCount,
|
|
3116
3148
|
"timeline windows",
|
|
3117
3149
|
budget
|
|
@@ -3120,13 +3152,13 @@ function analyzeTimeline(notes, opts = {}, previous, dirty) {
|
|
|
3120
3152
|
{
|
|
3121
3153
|
startBeat: musicStart,
|
|
3122
3154
|
endBeat: totalBeats,
|
|
3123
|
-
key:
|
|
3155
|
+
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, opts.key),
|
|
3124
3156
|
confidence: 1
|
|
3125
3157
|
}
|
|
3126
3158
|
] : keyTimelineFromNotes(sounding, { meters, totalBeats, budget });
|
|
3127
|
-
const prevailingKey = _nullishCoalesce(prevailingKeyOf(keys), () => (
|
|
3159
|
+
const prevailingKey = _nullishCoalesce(prevailingKeyOf(keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))));
|
|
3128
3160
|
const keyAt = keyLookup(keys, prevailingKey);
|
|
3129
|
-
const scaleAt = (beat) =>
|
|
3161
|
+
const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
|
|
3130
3162
|
const segments = [];
|
|
3131
3163
|
const segmentConfidence = [];
|
|
3132
3164
|
const slotNotes = bucketNotesBySlot(sounding, grid, slotCount, {
|
|
@@ -3200,7 +3232,7 @@ function analyzeTimeline(notes, opts = {}, previous, dirty) {
|
|
|
3200
3232
|
}
|
|
3201
3233
|
const { start, end } = bounds;
|
|
3202
3234
|
const last = segments[segments.length - 1];
|
|
3203
|
-
if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) <
|
|
3235
|
+
if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) < _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3204
3236
|
const lastLength = last.endBeat - last.startBeat;
|
|
3205
3237
|
const length = end - start;
|
|
3206
3238
|
const lastConfidence = _nullishCoalesce(segmentConfidence[segmentConfidence.length - 1], () => ( 0));
|
|
@@ -3241,11 +3273,11 @@ function detectCadences(timeline, key) {
|
|
|
3241
3273
|
if (!prev || !cur) {
|
|
3242
3274
|
continue;
|
|
3243
3275
|
}
|
|
3244
|
-
if (Math.abs(cur.startBeat - prev.endBeat) >
|
|
3276
|
+
if (Math.abs(cur.startBeat - prev.endBeat) > _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3245
3277
|
continue;
|
|
3246
3278
|
}
|
|
3247
3279
|
const before = timeline.segments[i - 2];
|
|
3248
|
-
const approach = before !== void 0 && Math.abs(prev.startBeat - before.endBeat) <=
|
|
3280
|
+
const approach = before !== void 0 && Math.abs(prev.startBeat - before.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS ? before.chord : void 0;
|
|
3249
3281
|
const key2 = keyAt(cur.startBeat);
|
|
3250
3282
|
const cadence = detectCadence(prev.chord, cur.chord, key2, {
|
|
3251
3283
|
...approach === void 0 ? {} : { approach }
|
|
@@ -3255,7 +3287,7 @@ function detectCadences(timeline, key) {
|
|
|
3255
3287
|
}
|
|
3256
3288
|
if (cadence.type === "half" && isCadentialSixFour(prev.chord, cur.chord, key2)) {
|
|
3257
3289
|
const next = timeline.segments[i + 1];
|
|
3258
|
-
if (next !== void 0 && Math.abs(next.startBeat - cur.endBeat) <=
|
|
3290
|
+
if (next !== void 0 && Math.abs(next.startBeat - cur.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3259
3291
|
const onward = detectCadence(cur.chord, next.chord, keyAt(next.startBeat)).type;
|
|
3260
3292
|
if (onward === "authentic" || onward === "deceptive") {
|
|
3261
3293
|
continue;
|
|
@@ -3267,6 +3299,1840 @@ function detectCadences(timeline, key) {
|
|
|
3267
3299
|
return hits;
|
|
3268
3300
|
}
|
|
3269
3301
|
|
|
3302
|
+
// src/analyze/melody/internal.ts
|
|
3303
|
+
var RHYTHM_GRID = 64;
|
|
3304
|
+
function phraseNotes(phrase) {
|
|
3305
|
+
if (Array.isArray(phrase)) {
|
|
3306
|
+
return phrase;
|
|
3307
|
+
}
|
|
3308
|
+
return _chunkS5BKZZGMcjs.assertRecord.call(void 0, phrase, "melodic phrase").notes;
|
|
3309
|
+
}
|
|
3310
|
+
function orderedNotes(notes, name, budget) {
|
|
3311
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, name, { allowNonPositiveDuration: true, budget });
|
|
3312
|
+
const sounding = notes.filter((note) => note.durationBeat > 0).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
|
|
3313
|
+
const line = [];
|
|
3314
|
+
for (const note of sounding) {
|
|
3315
|
+
const previous = line[line.length - 1];
|
|
3316
|
+
if (previous !== void 0 && Math.abs(note.startBeat - previous.startBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3317
|
+
line[line.length - 1] = note;
|
|
3318
|
+
continue;
|
|
3319
|
+
}
|
|
3320
|
+
line.push(note);
|
|
3321
|
+
}
|
|
3322
|
+
return line;
|
|
3323
|
+
}
|
|
3324
|
+
function intervalsOf(notes) {
|
|
3325
|
+
const out = [];
|
|
3326
|
+
for (let i = 1; i < notes.length; i += 1) {
|
|
3327
|
+
out.push((_nullishCoalesce(_optionalChain([notes, 'access', _57 => _57[i], 'optionalAccess', _58 => _58.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _59 => _59[i - 1], 'optionalAccess', _60 => _60.pitch]), () => ( 0))));
|
|
3328
|
+
}
|
|
3329
|
+
return out;
|
|
3330
|
+
}
|
|
3331
|
+
function onsetGaps(notes) {
|
|
3332
|
+
const out = [];
|
|
3333
|
+
for (let i = 1; i < notes.length; i += 1) {
|
|
3334
|
+
out.push((_nullishCoalesce(_optionalChain([notes, 'access', _61 => _61[i], 'optionalAccess', _62 => _62.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _63 => _63[i - 1], 'optionalAccess', _64 => _64.startBeat]), () => ( 0))));
|
|
3335
|
+
}
|
|
3336
|
+
return out;
|
|
3337
|
+
}
|
|
3338
|
+
function retrogradeGaps(notes) {
|
|
3339
|
+
const out = [];
|
|
3340
|
+
for (let i = notes.length - 1; i >= 1; i -= 1) {
|
|
3341
|
+
const later = notes[i];
|
|
3342
|
+
const earlier = notes[i - 1];
|
|
3343
|
+
out.push(
|
|
3344
|
+
(_nullishCoalesce(_optionalChain([later, 'optionalAccess', _65 => _65.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _66 => _66.durationBeat]), () => ( 0))) - ((_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _67 => _67.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _68 => _68.durationBeat]), () => ( 0))))
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
return out;
|
|
3348
|
+
}
|
|
3349
|
+
function rhythmProfile(gaps) {
|
|
3350
|
+
const unit = _nullishCoalesce(gaps[0], () => ( 0));
|
|
3351
|
+
if (!(unit > _chunkS5BKZZGMcjs.BEAT_EPS)) {
|
|
3352
|
+
return gaps.length === 0 ? [] : null;
|
|
3353
|
+
}
|
|
3354
|
+
return gaps.map((gap) => Math.round(gap / unit * RHYTHM_GRID) / RHYTHM_GRID);
|
|
3355
|
+
}
|
|
3356
|
+
function sameNumbers(a, b) {
|
|
3357
|
+
if (a.length !== b.length) {
|
|
3358
|
+
return false;
|
|
3359
|
+
}
|
|
3360
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
3361
|
+
if (Math.abs((_nullishCoalesce(a[i], () => ( 0))) - (_nullishCoalesce(b[i], () => ( 0)))) > _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
3362
|
+
return false;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
return true;
|
|
3366
|
+
}
|
|
3367
|
+
function roundTo(value, digits) {
|
|
3368
|
+
const scale = 10 ** digits;
|
|
3369
|
+
return Math.round(value * scale) / scale;
|
|
3370
|
+
}
|
|
3371
|
+
function endBeatOf(notes) {
|
|
3372
|
+
let end = 0;
|
|
3373
|
+
for (const note of notes) {
|
|
3374
|
+
end = Math.max(end, note.startBeat + note.durationBeat);
|
|
3375
|
+
}
|
|
3376
|
+
return end;
|
|
3377
|
+
}
|
|
3378
|
+
function onsetSpan(notes) {
|
|
3379
|
+
if (notes.length < 2) {
|
|
3380
|
+
return 0;
|
|
3381
|
+
}
|
|
3382
|
+
return (_nullishCoalesce(_optionalChain([notes, 'access', _69 => _69[notes.length - 1], 'optionalAccess', _70 => _70.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _71 => _71[0], 'optionalAccess', _72 => _72.startBeat]), () => ( 0)));
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
// src/analyze/melody/contour.ts
|
|
3386
|
+
var DIRECTIONAL_SHARE = 0.75;
|
|
3387
|
+
var MELODIC_CONTOUR_SHAPES = Object.freeze([
|
|
3388
|
+
"arch",
|
|
3389
|
+
"ascending",
|
|
3390
|
+
"descending",
|
|
3391
|
+
"wave",
|
|
3392
|
+
"static"
|
|
3393
|
+
]);
|
|
3394
|
+
function extremes(notes) {
|
|
3395
|
+
if (notes.length === 0) {
|
|
3396
|
+
return { peak: -1, trough: -1 };
|
|
3397
|
+
}
|
|
3398
|
+
let peak = 0;
|
|
3399
|
+
let trough = 0;
|
|
3400
|
+
for (let i = 1; i < notes.length; i += 1) {
|
|
3401
|
+
if ((_nullishCoalesce(_optionalChain([notes, 'access', _73 => _73[i], 'optionalAccess', _74 => _74.pitch]), () => ( 0))) > (_nullishCoalesce(_optionalChain([notes, 'access', _75 => _75[peak], 'optionalAccess', _76 => _76.pitch]), () => ( 0)))) {
|
|
3402
|
+
peak = i;
|
|
3403
|
+
}
|
|
3404
|
+
if ((_nullishCoalesce(_optionalChain([notes, 'access', _77 => _77[i], 'optionalAccess', _78 => _78.pitch]), () => ( 0))) < (_nullishCoalesce(_optionalChain([notes, 'access', _79 => _79[trough], 'optionalAccess', _80 => _80.pitch]), () => ( 0)))) {
|
|
3405
|
+
trough = i;
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
return { peak, trough };
|
|
3409
|
+
}
|
|
3410
|
+
function melodicContour(notes) {
|
|
3411
|
+
const line = orderedNotes(phraseNotes(notes), "contour notes");
|
|
3412
|
+
const intervals = intervalsOf(line);
|
|
3413
|
+
const directions = intervals.map(
|
|
3414
|
+
(step) => step > 0 ? "up" : step < 0 ? "down" : "same"
|
|
3415
|
+
);
|
|
3416
|
+
const { peak, trough } = extremes(line);
|
|
3417
|
+
const range = (_nullishCoalesce(_optionalChain([line, 'access', _81 => _81[peak], 'optionalAccess', _82 => _82.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([line, 'access', _83 => _83[trough], 'optionalAccess', _84 => _84.pitch]), () => ( 0)));
|
|
3418
|
+
const motion = intervals.reduce((sum, step) => sum + Math.abs(step), 0);
|
|
3419
|
+
if (motion === 0) {
|
|
3420
|
+
return {
|
|
3421
|
+
directions,
|
|
3422
|
+
shape: "static",
|
|
3423
|
+
peakIndex: peak,
|
|
3424
|
+
troughIndex: trough,
|
|
3425
|
+
range: 0,
|
|
3426
|
+
rationale: line.length < 2 ? "Too short to have a direction, so the line is static" : "Every note at the same pitch, so the line is static"
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3429
|
+
const first = _nullishCoalesce(_optionalChain([line, 'access', _85 => _85[0], 'optionalAccess', _86 => _86.pitch]), () => ( 0));
|
|
3430
|
+
const last = _nullishCoalesce(_optionalChain([line, 'access', _87 => _87[line.length - 1], 'optionalAccess', _88 => _88.pitch]), () => ( 0));
|
|
3431
|
+
const net = last - first;
|
|
3432
|
+
const directness = Math.abs(net) / motion;
|
|
3433
|
+
const share = Math.round(directness * 100);
|
|
3434
|
+
if (directness >= DIRECTIONAL_SHARE) {
|
|
3435
|
+
const shape = net > 0 ? "ascending" : "descending";
|
|
3436
|
+
return {
|
|
3437
|
+
directions,
|
|
3438
|
+
shape,
|
|
3439
|
+
peakIndex: peak,
|
|
3440
|
+
troughIndex: trough,
|
|
3441
|
+
range,
|
|
3442
|
+
rationale: `${share}% of the motion runs ${net > 0 ? "up" : "down"}, so the line is ${shape}`
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3445
|
+
const peakPitch = _nullishCoalesce(_optionalChain([line, 'access', _89 => _89[peak], 'optionalAccess', _90 => _90.pitch]), () => ( 0));
|
|
3446
|
+
const archMotion = peakPitch - first + (peakPitch - last);
|
|
3447
|
+
if (peak > 0 && peak < line.length - 1 && archMotion >= DIRECTIONAL_SHARE * motion) {
|
|
3448
|
+
return {
|
|
3449
|
+
directions,
|
|
3450
|
+
shape: "arch",
|
|
3451
|
+
peakIndex: peak,
|
|
3452
|
+
troughIndex: trough,
|
|
3453
|
+
range,
|
|
3454
|
+
rationale: `Rises to its peak at note ${peak + 1} of ${line.length} and falls back, so the line is an arch`
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
return {
|
|
3458
|
+
directions,
|
|
3459
|
+
shape: "wave",
|
|
3460
|
+
peakIndex: peak,
|
|
3461
|
+
troughIndex: trough,
|
|
3462
|
+
range,
|
|
3463
|
+
rationale: `Changes direction without settling either way (${share}% of the motion nets out), so the line is a wave`
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
// src/analyze/melody/motifs.ts
|
|
3468
|
+
var MIN_CELL_NOTES = 3;
|
|
3469
|
+
var MAX_CELL_NOTES = 8;
|
|
3470
|
+
var MIN_OCCURRENCES = 2;
|
|
3471
|
+
function windowSignature(notes) {
|
|
3472
|
+
const profile = rhythmProfile(onsetGaps(notes));
|
|
3473
|
+
if (profile === null) {
|
|
3474
|
+
return null;
|
|
3475
|
+
}
|
|
3476
|
+
return `${notes.length}|${intervalsOf(notes).join(",")}|${profile.join(",")}`;
|
|
3477
|
+
}
|
|
3478
|
+
function nonOverlapping(starts, length) {
|
|
3479
|
+
const picked = [];
|
|
3480
|
+
let lastEnd = Number.NEGATIVE_INFINITY;
|
|
3481
|
+
for (const start of starts) {
|
|
3482
|
+
if (start > lastEnd) {
|
|
3483
|
+
picked.push(start);
|
|
3484
|
+
lastEnd = start + length - 1;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
return picked;
|
|
3488
|
+
}
|
|
3489
|
+
function subsumes(outer, inner) {
|
|
3490
|
+
let at = 0;
|
|
3491
|
+
for (const start of inner.starts) {
|
|
3492
|
+
while (at + 1 < outer.starts.length && (_nullishCoalesce(outer.starts[at + 1], () => ( 0))) <= start) {
|
|
3493
|
+
at += 1;
|
|
3494
|
+
}
|
|
3495
|
+
const from = outer.starts[at];
|
|
3496
|
+
if (from === void 0 || start < from || start + inner.length > from + outer.length) {
|
|
3497
|
+
return false;
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
return true;
|
|
3501
|
+
}
|
|
3502
|
+
function listBeats(occurrences) {
|
|
3503
|
+
const shown = occurrences.slice(0, 5).map((occurrence) => roundTo(occurrence.startBeat, 3));
|
|
3504
|
+
return occurrences.length > shown.length ? `${shown.join(", ")}, and ${occurrences.length - shown.length} more` : shown.join(", ");
|
|
3505
|
+
}
|
|
3506
|
+
function motifFromGroup(group, notes) {
|
|
3507
|
+
const first = _nullishCoalesce(group.starts[0], () => ( 0));
|
|
3508
|
+
const prime = notes.slice(first, first + group.length).map((note) => ({ ...note }));
|
|
3509
|
+
const primePitch = _nullishCoalesce(_optionalChain([prime, 'access', _91 => _91[0], 'optionalAccess', _92 => _92.pitch]), () => ( 0));
|
|
3510
|
+
const primeSpan = onsetSpan(prime);
|
|
3511
|
+
const occurrences = group.starts.map((start) => {
|
|
3512
|
+
const statement = notes.slice(start, start + group.length);
|
|
3513
|
+
const span = onsetSpan(statement);
|
|
3514
|
+
return {
|
|
3515
|
+
noteIndex: start,
|
|
3516
|
+
startBeat: _nullishCoalesce(_optionalChain([statement, 'access', _93 => _93[0], 'optionalAccess', _94 => _94.startBeat]), () => ( 0)),
|
|
3517
|
+
endBeat: endBeatOf(statement),
|
|
3518
|
+
transpose: (_nullishCoalesce(_optionalChain([statement, 'access', _95 => _95[0], 'optionalAccess', _96 => _96.pitch]), () => ( 0))) - primePitch,
|
|
3519
|
+
timeRatio: primeSpan > _chunkS5BKZZGMcjs.BEAT_EPS ? span / primeSpan : 1
|
|
3520
|
+
};
|
|
3521
|
+
});
|
|
3522
|
+
const shape = melodicContour(prime).shape;
|
|
3523
|
+
const transposed = occurrences.some((occurrence) => occurrence.transpose !== 0);
|
|
3524
|
+
const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS);
|
|
3525
|
+
const detail = [
|
|
3526
|
+
transposed ? "at more than one pitch level" : "at the same pitch level",
|
|
3527
|
+
stretched ? "and in more than one set of note values" : ""
|
|
3528
|
+
].filter((part) => part !== "").join(" ");
|
|
3529
|
+
return {
|
|
3530
|
+
notes: prime,
|
|
3531
|
+
intervals: intervalsOf(prime),
|
|
3532
|
+
rhythm: _nullishCoalesce(rhythmProfile(onsetGaps(prime)), () => ( [])),
|
|
3533
|
+
occurrences,
|
|
3534
|
+
rationale: `${group.length}-note ${shape} cell stated ${occurrences.length} ${occurrences.length === 1 ? "time" : "times"} (beats ${listBeats(occurrences)}), ${detail}`
|
|
3535
|
+
};
|
|
3536
|
+
}
|
|
3537
|
+
function extractMotifs(notes, opts = {}) {
|
|
3538
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
3539
|
+
const minNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.minNotes, () => ( MIN_CELL_NOTES)), "motif minNotes", 2, 64);
|
|
3540
|
+
const maxNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.maxNotes, () => ( MAX_CELL_NOTES)), "motif maxNotes", 2, 64);
|
|
3541
|
+
if (maxNotes < minNotes) {
|
|
3542
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
3543
|
+
`motif maxNotes must be at least minNotes ${minNotes}; received ${maxNotes}`
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
3546
|
+
const minOccurrences = _chunkS5BKZZGMcjs.assertInteger.call(void 0,
|
|
3547
|
+
_nullishCoalesce(asked.minOccurrences, () => ( MIN_OCCURRENCES)),
|
|
3548
|
+
"motif minOccurrences",
|
|
3549
|
+
2
|
|
3550
|
+
);
|
|
3551
|
+
const sounding = orderedNotes(notes, "melody notes", asked.budget);
|
|
3552
|
+
if (sounding.length < minNotes) {
|
|
3553
|
+
return [];
|
|
3554
|
+
}
|
|
3555
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
|
|
3556
|
+
sounding.length * (maxNotes - minNotes + 1),
|
|
3557
|
+
"motif windows",
|
|
3558
|
+
asked.budget
|
|
3559
|
+
);
|
|
3560
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3561
|
+
for (let length = minNotes; length <= maxNotes; length += 1) {
|
|
3562
|
+
for (let start = 0; start + length <= sounding.length; start += 1) {
|
|
3563
|
+
const signature = windowSignature(sounding.slice(start, start + length));
|
|
3564
|
+
if (signature === null) {
|
|
3565
|
+
continue;
|
|
3566
|
+
}
|
|
3567
|
+
const group = groups.get(signature);
|
|
3568
|
+
if (group === void 0) {
|
|
3569
|
+
groups.set(signature, { signature, length, starts: [start] });
|
|
3570
|
+
} else {
|
|
3571
|
+
group.starts.push(start);
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
const candidates = [...groups.values()].map((group) => ({ ...group, starts: nonOverlapping(group.starts, group.length) })).filter((group) => group.starts.length >= minOccurrences).sort(
|
|
3576
|
+
(a, b) => b.length - a.length || b.starts.length - a.starts.length || (_nullishCoalesce(a.starts[0], () => ( 0))) - (_nullishCoalesce(b.starts[0], () => ( 0))) || (a.signature < b.signature ? -1 : a.signature > b.signature ? 1 : 0)
|
|
3577
|
+
);
|
|
3578
|
+
const keptByLength = /* @__PURE__ */ new Map();
|
|
3579
|
+
const kept = [];
|
|
3580
|
+
let comparisons = 0;
|
|
3581
|
+
for (const candidate of candidates) {
|
|
3582
|
+
const first = _nullishCoalesce(candidate.starts[0], () => ( 0));
|
|
3583
|
+
let covered = false;
|
|
3584
|
+
for (let length = candidate.length + 1; length <= maxNotes && !covered; length += 1) {
|
|
3585
|
+
const byStart = keptByLength.get(length);
|
|
3586
|
+
if (byStart === void 0) {
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
for (let from = Math.max(0, first - (length - candidate.length)); from <= first; from += 1) {
|
|
3590
|
+
comparisons += 1;
|
|
3591
|
+
const outer = byStart.get(from);
|
|
3592
|
+
if (outer === void 0 || outer.starts.length < candidate.starts.length) {
|
|
3593
|
+
continue;
|
|
3594
|
+
}
|
|
3595
|
+
comparisons += outer.starts.length + candidate.starts.length;
|
|
3596
|
+
if (subsumes(outer, candidate)) {
|
|
3597
|
+
covered = true;
|
|
3598
|
+
break;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "motif subsumption comparisons", asked.budget);
|
|
3603
|
+
if (!covered) {
|
|
3604
|
+
kept.push(candidate);
|
|
3605
|
+
let byStart = keptByLength.get(candidate.length);
|
|
3606
|
+
if (byStart === void 0) {
|
|
3607
|
+
byStart = /* @__PURE__ */ new Map();
|
|
3608
|
+
keptByLength.set(candidate.length, byStart);
|
|
3609
|
+
}
|
|
3610
|
+
for (const start of candidate.starts) {
|
|
3611
|
+
byStart.set(start, candidate);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
return kept.map((group) => motifFromGroup(group, sounding));
|
|
3616
|
+
}
|
|
3617
|
+
function motifFromNotes(notes) {
|
|
3618
|
+
const sounding = orderedNotes(notes, "motif notes").map((note) => ({ ...note }));
|
|
3619
|
+
return motifFromGroup(
|
|
3620
|
+
{ signature: _nullishCoalesce(windowSignature(sounding), () => ( "")), length: sounding.length, starts: [0] },
|
|
3621
|
+
sounding
|
|
3622
|
+
);
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
// src/analyze/melody/relation.ts
|
|
3626
|
+
var MOTIF_RELATION_KINDS = Object.freeze([
|
|
3627
|
+
"repetition",
|
|
3628
|
+
"transposition",
|
|
3629
|
+
"tonalTransposition",
|
|
3630
|
+
"inversion",
|
|
3631
|
+
"retrograde",
|
|
3632
|
+
"retrogradeInversion",
|
|
3633
|
+
"augmentation",
|
|
3634
|
+
"diminution"
|
|
3635
|
+
]);
|
|
3636
|
+
function spellingOf(key) {
|
|
3637
|
+
if (key === void 0) {
|
|
3638
|
+
return "sharp";
|
|
3639
|
+
}
|
|
3640
|
+
return _chunk3R3QVKY5cjs.keySignatureFifths.call(void 0, key.tonic, key.scale) < 0 ? "flat" : "sharp";
|
|
3641
|
+
}
|
|
3642
|
+
function intervalBetween(from, to, key) {
|
|
3643
|
+
const spelling = spellingOf(key);
|
|
3644
|
+
return _chunkS5BKZZGMcjs.spelledInterval.call(void 0, _chunkS5BKZZGMcjs.midiToNote.call(void 0, from, spelling), _chunkS5BKZZGMcjs.midiToNote.call(void 0, to, spelling));
|
|
3645
|
+
}
|
|
3646
|
+
function intervalName(interval) {
|
|
3647
|
+
return `${interval.descending && interval.semitones !== 0 ? "-" : ""}${interval.quality}${interval.number}`;
|
|
3648
|
+
}
|
|
3649
|
+
function ladderIndices(notes, key) {
|
|
3650
|
+
const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
3651
|
+
const offsets = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pc - root));
|
|
3652
|
+
if (offsets.length === 0) {
|
|
3653
|
+
return null;
|
|
3654
|
+
}
|
|
3655
|
+
const out = [];
|
|
3656
|
+
for (const note of notes) {
|
|
3657
|
+
const semitones = note.pitch - root;
|
|
3658
|
+
const octave = Math.floor(semitones / 12);
|
|
3659
|
+
const degree = offsets.indexOf(semitones - octave * 12);
|
|
3660
|
+
if (degree < 0) {
|
|
3661
|
+
return null;
|
|
3662
|
+
}
|
|
3663
|
+
out.push(octave * offsets.length + degree);
|
|
3664
|
+
}
|
|
3665
|
+
return out;
|
|
3666
|
+
}
|
|
3667
|
+
function ladderSteps(indices) {
|
|
3668
|
+
const out = [];
|
|
3669
|
+
for (let i = 1; i < indices.length; i += 1) {
|
|
3670
|
+
out.push((_nullishCoalesce(indices[i], () => ( 0))) - (_nullishCoalesce(indices[i - 1], () => ( 0))));
|
|
3671
|
+
}
|
|
3672
|
+
return out;
|
|
3673
|
+
}
|
|
3674
|
+
function pitchShapes(a, b) {
|
|
3675
|
+
const shapes = [];
|
|
3676
|
+
if (sameNumbers(a, b)) {
|
|
3677
|
+
shapes.push("exact");
|
|
3678
|
+
}
|
|
3679
|
+
if (sameNumbers(
|
|
3680
|
+
b,
|
|
3681
|
+
a.map((step) => -step)
|
|
3682
|
+
)) {
|
|
3683
|
+
shapes.push("inversion");
|
|
3684
|
+
}
|
|
3685
|
+
const reversed = [...a].reverse();
|
|
3686
|
+
if (sameNumbers(
|
|
3687
|
+
b,
|
|
3688
|
+
reversed.map((step) => -step)
|
|
3689
|
+
)) {
|
|
3690
|
+
shapes.push("retrograde");
|
|
3691
|
+
}
|
|
3692
|
+
if (sameNumbers(b, reversed)) {
|
|
3693
|
+
shapes.push("retrogradeInversion");
|
|
3694
|
+
}
|
|
3695
|
+
return shapes;
|
|
3696
|
+
}
|
|
3697
|
+
function totalGap(gaps) {
|
|
3698
|
+
return gaps.reduce((sum, gap) => sum + gap, 0);
|
|
3699
|
+
}
|
|
3700
|
+
function stretchRatio(modelGaps, answerGaps) {
|
|
3701
|
+
const span = totalGap(modelGaps);
|
|
3702
|
+
return span > _chunkS5BKZZGMcjs.BEAT_EPS ? totalGap(answerGaps) / span : 1;
|
|
3703
|
+
}
|
|
3704
|
+
function stretchPhrase(ratio2) {
|
|
3705
|
+
return `note values ${roundTo(ratio2, 3)}x the model's`;
|
|
3706
|
+
}
|
|
3707
|
+
function relateMotifs(a, b, keyLike) {
|
|
3708
|
+
const key = keyLike === void 0 ? void 0 : _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
3709
|
+
const model = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, a, "a").notes, "a.notes");
|
|
3710
|
+
const answer = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, b, "b").notes, "b.notes");
|
|
3711
|
+
if (model.length === 0 || model.length !== answer.length) {
|
|
3712
|
+
return null;
|
|
3713
|
+
}
|
|
3714
|
+
const modelGaps = onsetGaps(model);
|
|
3715
|
+
const answerGaps = onsetGaps(answer);
|
|
3716
|
+
const modelProfile = rhythmProfile(modelGaps);
|
|
3717
|
+
const answerProfile = rhythmProfile(answerGaps);
|
|
3718
|
+
const retroGaps = retrogradeGaps(model);
|
|
3719
|
+
const retroProfile = rhythmProfile(retroGaps);
|
|
3720
|
+
if (modelProfile === null || answerProfile === null) {
|
|
3721
|
+
return null;
|
|
3722
|
+
}
|
|
3723
|
+
const forward = sameNumbers(modelProfile, answerProfile);
|
|
3724
|
+
const backward = retroProfile !== null && sameNumbers(retroProfile, answerProfile);
|
|
3725
|
+
const semitones = (_nullishCoalesce(_optionalChain([answer, 'access', _97 => _97[0], 'optionalAccess', _98 => _98.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([model, 'access', _99 => _99[0], 'optionalAccess', _100 => _100.pitch]), () => ( 0)));
|
|
3726
|
+
const interval = intervalBetween(_nullishCoalesce(_optionalChain([model, 'access', _101 => _101[0], 'optionalAccess', _102 => _102.pitch]), () => ( 0)), _nullishCoalesce(_optionalChain([answer, 'access', _103 => _103[0], 'optionalAccess', _104 => _104.pitch]), () => ( 0)), key);
|
|
3727
|
+
const gap = (_nullishCoalesce(_optionalChain([answer, 'access', _105 => _105[0], 'optionalAccess', _106 => _106.startBeat]), () => ( 0))) - endBeatOf(model);
|
|
3728
|
+
const sequence = gap >= -HUMANIZE_ADJACENCY && gap <= HUMANIZE_ADJACENCY;
|
|
3729
|
+
const timeRatio = stretchRatio(modelGaps, answerGaps);
|
|
3730
|
+
const stretched = Math.abs(timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
3731
|
+
const shapes = pitchShapes(intervalsOf(model), intervalsOf(answer));
|
|
3732
|
+
const base = { sequence, semitones, interval, timeRatio };
|
|
3733
|
+
const backwardBase = { ...base, timeRatio: stretchRatio(retroGaps, answerGaps) };
|
|
3734
|
+
if (forward && shapes.includes("exact")) {
|
|
3735
|
+
if (semitones === 0) {
|
|
3736
|
+
if (!stretched) {
|
|
3737
|
+
return {
|
|
3738
|
+
...base,
|
|
3739
|
+
kind: "repetition",
|
|
3740
|
+
rationale: "Exact repetition: the same pitches in the same rhythm"
|
|
3741
|
+
};
|
|
3742
|
+
}
|
|
3743
|
+
const augmented = timeRatio > 1;
|
|
3744
|
+
return {
|
|
3745
|
+
...base,
|
|
3746
|
+
kind: augmented ? "augmentation" : "diminution",
|
|
3747
|
+
rationale: `${augmented ? "Augmentation" : "Diminution"}: the same pitches in ${stretchPhrase(timeRatio)}`
|
|
3748
|
+
};
|
|
3749
|
+
}
|
|
3750
|
+
const name = intervalName(interval);
|
|
3751
|
+
return {
|
|
3752
|
+
...base,
|
|
3753
|
+
kind: "transposition",
|
|
3754
|
+
rationale: sequence ? `Real sequence: the cell restated ${name} away with every interval kept literally` + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "") : `Transposition by ${name}: every interval kept literally` + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
|
|
3755
|
+
};
|
|
3756
|
+
}
|
|
3757
|
+
if (forward && key !== void 0) {
|
|
3758
|
+
const modelLadder = ladderIndices(model, key.scale);
|
|
3759
|
+
const answerLadder = ladderIndices(answer, key.scale);
|
|
3760
|
+
if (modelLadder !== null && answerLadder !== null) {
|
|
3761
|
+
const degrees = (_nullishCoalesce(answerLadder[0], () => ( 0))) - (_nullishCoalesce(modelLadder[0], () => ( 0)));
|
|
3762
|
+
if (degrees !== 0 && sameNumbers(ladderSteps(modelLadder), ladderSteps(answerLadder))) {
|
|
3763
|
+
const direction = degrees > 0 ? "higher" : "lower";
|
|
3764
|
+
const count = Math.abs(degrees);
|
|
3765
|
+
return {
|
|
3766
|
+
...base,
|
|
3767
|
+
kind: "tonalTransposition",
|
|
3768
|
+
degrees,
|
|
3769
|
+
rationale: `${sequence ? "Tonal sequence" : "Tonal transposition"}: the cell restated ${count} scale ${count === 1 ? "degree" : "degrees"} ${direction} inside the key, so the key resizes its intervals \u2014 a real one would have kept them literal`
|
|
3770
|
+
};
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
if (forward && shapes.includes("inversion")) {
|
|
3775
|
+
return {
|
|
3776
|
+
...base,
|
|
3777
|
+
kind: "inversion",
|
|
3778
|
+
rationale: "Inversion: every interval turned upside down about the first note" + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
|
|
3779
|
+
};
|
|
3780
|
+
}
|
|
3781
|
+
if (backward && shapes.includes("retrograde")) {
|
|
3782
|
+
return {
|
|
3783
|
+
...backwardBase,
|
|
3784
|
+
kind: "retrograde",
|
|
3785
|
+
rationale: "Retrograde: the cell read back to front, rhythm included"
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
if (backward && shapes.includes("retrogradeInversion")) {
|
|
3789
|
+
return {
|
|
3790
|
+
...backwardBase,
|
|
3791
|
+
kind: "retrogradeInversion",
|
|
3792
|
+
rationale: "Retrograde inversion: the cell read back to front and turned upside down"
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
return null;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
// src/analyze/sequence.ts
|
|
3799
|
+
function gradedEditDistance(a, b, cost, gap = 1) {
|
|
3800
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i * gap);
|
|
3801
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
3802
|
+
const row = new Array(b.length + 1);
|
|
3803
|
+
row[0] = i * gap;
|
|
3804
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
3805
|
+
const ai = a[i - 1];
|
|
3806
|
+
const bj = b[j - 1];
|
|
3807
|
+
const substitution = (_nullishCoalesce(previous[j - 1], () => ( 0))) + cost(ai, bj);
|
|
3808
|
+
const deletion = (_nullishCoalesce(previous[j], () => ( 0))) + gap;
|
|
3809
|
+
const insertion = (_nullishCoalesce(row[j - 1], () => ( 0))) + gap;
|
|
3810
|
+
row[j] = Math.min(substitution, deletion, insertion);
|
|
3811
|
+
}
|
|
3812
|
+
previous = row;
|
|
3813
|
+
}
|
|
3814
|
+
return _nullishCoalesce(previous[b.length], () => ( 0));
|
|
3815
|
+
}
|
|
3816
|
+
function distanceToSimilarity(distance, a, b) {
|
|
3817
|
+
const longest = Math.max(a, b);
|
|
3818
|
+
if (longest === 0) {
|
|
3819
|
+
return 1;
|
|
3820
|
+
}
|
|
3821
|
+
return Math.min(1, Math.max(0, 1 - distance / longest));
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
// src/analyze/melody/similarity.ts
|
|
3825
|
+
var INTERVAL_TOLERANCE = 6;
|
|
3826
|
+
var PITCH_WEIGHT = 0.75;
|
|
3827
|
+
var RHYTHM_WEIGHT = 0.25;
|
|
3828
|
+
function intervalCost(x, y) {
|
|
3829
|
+
return Math.min(1, Math.abs(x - y) / INTERVAL_TOLERANCE);
|
|
3830
|
+
}
|
|
3831
|
+
function gapCost(x, y) {
|
|
3832
|
+
if (!(x > _chunkS5BKZZGMcjs.BEAT_EPS) || !(y > _chunkS5BKZZGMcjs.BEAT_EPS)) {
|
|
3833
|
+
return Math.abs(x - y) > _chunkS5BKZZGMcjs.BEAT_EPS ? 1 : 0;
|
|
3834
|
+
}
|
|
3835
|
+
return Math.min(1, Math.abs(Math.log2(x / y)));
|
|
3836
|
+
}
|
|
3837
|
+
function compareMelodies(a, b) {
|
|
3838
|
+
const left = orderedNotes(phraseNotes(a), "melody a");
|
|
3839
|
+
const right = orderedNotes(phraseNotes(b), "melody b");
|
|
3840
|
+
const leftIntervals = intervalsOf(left);
|
|
3841
|
+
const rightIntervals = intervalsOf(right);
|
|
3842
|
+
const pitchSimilarity = distanceToSimilarity(
|
|
3843
|
+
gradedEditDistance(leftIntervals, rightIntervals, intervalCost),
|
|
3844
|
+
leftIntervals.length,
|
|
3845
|
+
rightIntervals.length
|
|
3846
|
+
);
|
|
3847
|
+
const leftGaps = _nullishCoalesce(rhythmProfile(onsetGaps(left)), () => ( onsetGaps(left)));
|
|
3848
|
+
const rightGaps = _nullishCoalesce(rhythmProfile(onsetGaps(right)), () => ( onsetGaps(right)));
|
|
3849
|
+
const rhythmSimilarity = distanceToSimilarity(
|
|
3850
|
+
gradedEditDistance(leftGaps, rightGaps, gapCost),
|
|
3851
|
+
leftGaps.length,
|
|
3852
|
+
rightGaps.length
|
|
3853
|
+
);
|
|
3854
|
+
const similarity = PITCH_WEIGHT * pitchSimilarity + RHYTHM_WEIGHT * rhythmSimilarity;
|
|
3855
|
+
return {
|
|
3856
|
+
similarity,
|
|
3857
|
+
pitchSimilarity,
|
|
3858
|
+
rhythmSimilarity,
|
|
3859
|
+
rationale: `Interval shapes ${Math.round(pitchSimilarity * 100)}% alike, rhythms ${Math.round(rhythmSimilarity * 100)}% alike, weighted ${PITCH_WEIGHT}/${RHYTHM_WEIGHT} into ${roundTo(similarity, 3)}`
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
function melodicSimilarity(a, b) {
|
|
3863
|
+
return compareMelodies(a, b).similarity;
|
|
3864
|
+
}
|
|
3865
|
+
|
|
3866
|
+
// src/analyze/melody/graph.ts
|
|
3867
|
+
var DEFAULT_VARIATION_THRESHOLD = 0.75;
|
|
3868
|
+
function summarize(relation) {
|
|
3869
|
+
const { kind, sequence, semitones, degrees, timeRatio } = relation;
|
|
3870
|
+
return { kind, sequence, semitones, degrees, timeRatio };
|
|
3871
|
+
}
|
|
3872
|
+
function betterParent(candidate, current) {
|
|
3873
|
+
const candidateNamed = candidate.relation !== null;
|
|
3874
|
+
const currentNamed = current.relation !== null;
|
|
3875
|
+
if (candidateNamed !== currentNamed) {
|
|
3876
|
+
return candidateNamed;
|
|
3877
|
+
}
|
|
3878
|
+
if (candidate.similarity !== current.similarity) {
|
|
3879
|
+
return candidate.similarity > current.similarity;
|
|
3880
|
+
}
|
|
3881
|
+
if (candidate.startBeat !== current.startBeat) {
|
|
3882
|
+
return candidate.startBeat > current.startBeat;
|
|
3883
|
+
}
|
|
3884
|
+
return candidate.index < current.index;
|
|
3885
|
+
}
|
|
3886
|
+
function motifGraph(line, motifs, opts = {}) {
|
|
3887
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
3888
|
+
const variationThreshold = _nullishCoalesce(asked.variationThreshold, () => ( DEFAULT_VARIATION_THRESHOLD));
|
|
3889
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, variationThreshold, 0, 1, "motif graph variationThreshold");
|
|
3890
|
+
const ordered = orderedNotes(line, "line", asked.budget);
|
|
3891
|
+
const motifList = _chunkS5BKZZGMcjs.assertArray.call(void 0, motifs, "motifs");
|
|
3892
|
+
const infos = [];
|
|
3893
|
+
for (let mi = 0; mi < motifList.length; mi += 1) {
|
|
3894
|
+
const motif = _chunkS5BKZZGMcjs.assertRecord.call(void 0, motifList[mi], `motifs[${mi}]`);
|
|
3895
|
+
const notes = _chunkS5BKZZGMcjs.assertArray.call(void 0, motif.notes, `motifs[${mi}].notes`);
|
|
3896
|
+
const occurrences = _chunkS5BKZZGMcjs.assertArray.call(void 0,
|
|
3897
|
+
motif.occurrences,
|
|
3898
|
+
`motifs[${mi}].occurrences`
|
|
3899
|
+
);
|
|
3900
|
+
for (let oi = 0; oi < occurrences.length; oi += 1) {
|
|
3901
|
+
const occurrence = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
|
|
3902
|
+
occurrences[oi],
|
|
3903
|
+
`motifs[${mi}].occurrences[${oi}]`
|
|
3904
|
+
);
|
|
3905
|
+
const { noteIndex } = occurrence;
|
|
3906
|
+
const onLine = Number.isInteger(noteIndex) && noteIndex >= 0 && noteIndex + notes.length <= ordered.length && Math.abs((_nullishCoalesce(_optionalChain([ordered, 'access', _107 => _107[noteIndex], 'optionalAccess', _108 => _108.startBeat]), () => ( Number.NaN))) - occurrence.startBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
3907
|
+
if (!onLine) {
|
|
3908
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
3909
|
+
`motif occurrence motifs[${mi}].occurrences[${oi}] does not lie on the line`
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
infos.push({
|
|
3913
|
+
node: {
|
|
3914
|
+
motif: mi,
|
|
3915
|
+
occurrence: oi,
|
|
3916
|
+
startBeat: occurrence.startBeat,
|
|
3917
|
+
endBeat: occurrence.endBeat
|
|
3918
|
+
},
|
|
3919
|
+
data: motifFromNotes(ordered.slice(noteIndex, noteIndex + notes.length))
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
infos.sort((a, b) => a.node.startBeat - b.node.startBeat || a.node.motif - b.node.motif);
|
|
3924
|
+
const nodeCount = infos.length;
|
|
3925
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, nodeCount * nodeCount, "motif graph comparisons", asked.budget);
|
|
3926
|
+
const edges = [];
|
|
3927
|
+
for (let to = 1; to < nodeCount; to += 1) {
|
|
3928
|
+
const toInfo = infos[to];
|
|
3929
|
+
let best = null;
|
|
3930
|
+
for (let from = 0; from < to; from += 1) {
|
|
3931
|
+
const fromInfo = infos[from];
|
|
3932
|
+
const candidate = {
|
|
3933
|
+
index: from,
|
|
3934
|
+
startBeat: fromInfo.node.startBeat,
|
|
3935
|
+
relation: relateMotifs(fromInfo.data, toInfo.data, asked.key),
|
|
3936
|
+
similarity: melodicSimilarity(fromInfo.data, toInfo.data)
|
|
3937
|
+
};
|
|
3938
|
+
if (best === null || betterParent(candidate, best)) {
|
|
3939
|
+
best = candidate;
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
if (best !== null && (best.relation !== null || best.similarity >= variationThreshold)) {
|
|
3943
|
+
edges.push({
|
|
3944
|
+
from: best.index,
|
|
3945
|
+
to,
|
|
3946
|
+
relation: best.relation === null ? null : summarize(best.relation),
|
|
3947
|
+
similarity: best.similarity
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
return { nodes: infos.map((info) => info.node), edges };
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
// src/analyze/reduction/index.ts
|
|
3955
|
+
var REDUCTION_LEVELS = Object.freeze(["structural", "passing", "neighbor"]);
|
|
3956
|
+
var TONIC_ARRIVING = /* @__PURE__ */ new Set(["authentic", "plagal", "modal", "deceptive"]);
|
|
3957
|
+
function signedStep(fromPc, toPc) {
|
|
3958
|
+
return (_chunkS5BKZZGMcjs.pitchClassOf.call(void 0, toPc) - _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, fromPc) + 18) % 12 - 6;
|
|
3959
|
+
}
|
|
3960
|
+
function isStep2(distance) {
|
|
3961
|
+
const size = Math.abs(distance);
|
|
3962
|
+
return size === 1 || size === 2;
|
|
3963
|
+
}
|
|
3964
|
+
function touches(before, after) {
|
|
3965
|
+
return Math.abs(after.startBeat - before.endBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
3966
|
+
}
|
|
3967
|
+
function salienceFloors(durations) {
|
|
3968
|
+
const order = durations.map((_, index) => index).sort((a, b) => {
|
|
3969
|
+
const left = _nullishCoalesce(durations[a], () => ( 0));
|
|
3970
|
+
const right = _nullishCoalesce(durations[b], () => ( 0));
|
|
3971
|
+
return left - right || a - b;
|
|
3972
|
+
});
|
|
3973
|
+
const rankOf = new Array(durations.length).fill(0);
|
|
3974
|
+
for (let rank = 0; rank < order.length; rank += 1) {
|
|
3975
|
+
rankOf[_nullishCoalesce(order[rank], () => ( 0))] = rank;
|
|
3976
|
+
}
|
|
3977
|
+
const floors = [];
|
|
3978
|
+
const remaining = durations.length - 1;
|
|
3979
|
+
for (let index = 0; index < durations.length; index += 1) {
|
|
3980
|
+
if (remaining <= 0) {
|
|
3981
|
+
floors.push(0);
|
|
3982
|
+
continue;
|
|
3983
|
+
}
|
|
3984
|
+
const skipped = _nullishCoalesce(rankOf[index], () => ( 0));
|
|
3985
|
+
const at = (position) => _nullishCoalesce(durations[_nullishCoalesce(order[position < skipped ? position : position + 1], () => ( 0))], () => ( 0));
|
|
3986
|
+
const middle = remaining >> 1;
|
|
3987
|
+
floors.push(remaining % 2 === 1 ? at(middle) : (at(middle - 1) + at(middle)) / 2);
|
|
3988
|
+
}
|
|
3989
|
+
return floors;
|
|
3990
|
+
}
|
|
3991
|
+
function isTonicChord(chord, key) {
|
|
3992
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 0 && !isAppliedDominant(chord, key);
|
|
3993
|
+
}
|
|
3994
|
+
function functionalReason(segment, before, after, keyAt) {
|
|
3995
|
+
const key = keyAt(segment.startBeat);
|
|
3996
|
+
if (after !== void 0 && isCadentialSixFour(segment.chord, after.chord, key)) {
|
|
3997
|
+
return { kind: "cadentialSixFour" };
|
|
3998
|
+
}
|
|
3999
|
+
if (isTonicChord(segment.chord, key)) {
|
|
4000
|
+
return { kind: "tonic" };
|
|
4001
|
+
}
|
|
4002
|
+
if (_chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, segment.chord, key)) {
|
|
4003
|
+
return { kind: "dominant" };
|
|
4004
|
+
}
|
|
4005
|
+
if (before !== void 0) {
|
|
4006
|
+
const arrival = detectCadence(before.chord, segment.chord, key).type;
|
|
4007
|
+
if (arrival !== null) {
|
|
4008
|
+
return { kind: "cadenceArrival", cadence: arrival };
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
if (after !== void 0) {
|
|
4012
|
+
const agent = detectCadence(segment.chord, after.chord, keyAt(after.startBeat)).type;
|
|
4013
|
+
if (agent !== null && TONIC_ARRIVING.has(agent)) {
|
|
4014
|
+
return { kind: "cadenceAgent", cadence: agent };
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
4019
|
+
function figureReason(segment, before, after) {
|
|
4020
|
+
if (before === void 0 || after === void 0) {
|
|
4021
|
+
return null;
|
|
4022
|
+
}
|
|
4023
|
+
const incoming = signedStep(before.chord.rootPc, segment.chord.rootPc);
|
|
4024
|
+
const outgoing = signedStep(segment.chord.rootPc, after.chord.rootPc);
|
|
4025
|
+
if (!isStep2(incoming) || !isStep2(outgoing)) {
|
|
4026
|
+
return null;
|
|
4027
|
+
}
|
|
4028
|
+
if (incoming > 0 === outgoing > 0) {
|
|
4029
|
+
return { kind: "passing" };
|
|
4030
|
+
}
|
|
4031
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, before.chord.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, after.chord.rootPc) ? { kind: "neighbor" } : null;
|
|
4032
|
+
}
|
|
4033
|
+
function describe2(reason) {
|
|
4034
|
+
switch (reason.kind) {
|
|
4035
|
+
case "tonic":
|
|
4036
|
+
return "Structural: the tonic of the key, which the progression is heard against";
|
|
4037
|
+
case "dominant":
|
|
4038
|
+
return "Structural: the dominant of the key, the chord its tonic is approached from";
|
|
4039
|
+
case "cadentialSixFour":
|
|
4040
|
+
return "Structural: a tonic six-four on the bass of the dominant that follows, which prolongs that dominant rather than framing the tonic";
|
|
4041
|
+
case "cadenceArrival":
|
|
4042
|
+
return `Structural: the arrival of a ${reason.cadence} cadence`;
|
|
4043
|
+
case "cadenceAgent":
|
|
4044
|
+
return `Structural: the chord that drives a ${reason.cadence} cadence`;
|
|
4045
|
+
case "salient":
|
|
4046
|
+
return "Structural: it holds the harmony longer than the chords around it";
|
|
4047
|
+
case "first":
|
|
4048
|
+
return "Structural: the first chord of the progression frames it";
|
|
4049
|
+
case "last":
|
|
4050
|
+
return "Structural: the last chord of the progression frames it";
|
|
4051
|
+
case "unfigured":
|
|
4052
|
+
return "Structural: its root neither steps through to the next chord nor returns to the last, so it embellishes nothing";
|
|
4053
|
+
case "passing":
|
|
4054
|
+
return "Passing: its root steps in from the previous chord and on to the next in the same direction";
|
|
4055
|
+
case "neighbor":
|
|
4056
|
+
return "Neighbor: its root steps away from the surrounding harmony and returns to it";
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
var BASIS_READINGS = {
|
|
4060
|
+
function: ({ segment, before, after, keyAt }) => functionalReason(segment, before, after, keyAt),
|
|
4061
|
+
duration: ({ segment, salienceFloor }) => segment.endBeat - segment.startBeat > salienceFloor ? { kind: "salient" } : null
|
|
4062
|
+
};
|
|
4063
|
+
var REDUCTION_BASES = Object.keys(BASIS_READINGS);
|
|
4064
|
+
function levelOf(reason) {
|
|
4065
|
+
if (reason.kind === "passing") {
|
|
4066
|
+
return "passing";
|
|
4067
|
+
}
|
|
4068
|
+
return reason.kind === "neighbor" ? "neighbor" : "structural";
|
|
4069
|
+
}
|
|
4070
|
+
function reduceProgression(timeline, key, opts = {}) {
|
|
4071
|
+
assertChordTimeline(timeline);
|
|
4072
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
4073
|
+
const keyAt = keyScaleAt(key);
|
|
4074
|
+
const basis = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.basis, () => ( "function")), REDUCTION_BASES, "reduction basis");
|
|
4075
|
+
const reading = BASIS_READINGS[basis];
|
|
4076
|
+
const segments = timeline.segments;
|
|
4077
|
+
const floors = basis === "duration" ? salienceFloors(segments.map((segment) => segment.endBeat - segment.startBeat)) : [];
|
|
4078
|
+
const result = [];
|
|
4079
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
4080
|
+
const segment = segments[index];
|
|
4081
|
+
if (segment === void 0) {
|
|
4082
|
+
continue;
|
|
4083
|
+
}
|
|
4084
|
+
const previous = segments[index - 1];
|
|
4085
|
+
const following = segments[index + 1];
|
|
4086
|
+
const before = previous !== void 0 && touches(previous, segment) ? previous : void 0;
|
|
4087
|
+
const after = following !== void 0 && touches(segment, following) ? following : void 0;
|
|
4088
|
+
let reason = reading({ segment, before, after, keyAt, salienceFloor: _nullishCoalesce(floors[index], () => ( 0)) });
|
|
4089
|
+
if (reason === null && index === 0) {
|
|
4090
|
+
reason = { kind: "first" };
|
|
4091
|
+
}
|
|
4092
|
+
if (reason === null && index === segments.length - 1) {
|
|
4093
|
+
reason = { kind: "last" };
|
|
4094
|
+
}
|
|
4095
|
+
reason ??= _nullishCoalesce(figureReason(segment, before, after), () => ( { kind: "unfigured" }));
|
|
4096
|
+
result.push({
|
|
4097
|
+
chord: segment.chord,
|
|
4098
|
+
startBeat: segment.startBeat,
|
|
4099
|
+
endBeat: segment.endBeat,
|
|
4100
|
+
level: levelOf(reason),
|
|
4101
|
+
rationale: describe2(reason)
|
|
4102
|
+
});
|
|
4103
|
+
}
|
|
4104
|
+
return result;
|
|
4105
|
+
}
|
|
4106
|
+
|
|
4107
|
+
// src/analyze/rhythm/internal.ts
|
|
4108
|
+
function isMultipleOf(value, unit) {
|
|
4109
|
+
if (unit <= 0) {
|
|
4110
|
+
return false;
|
|
4111
|
+
}
|
|
4112
|
+
const ratio2 = value / unit;
|
|
4113
|
+
return Math.abs(ratio2 - Math.round(ratio2)) < _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
4114
|
+
}
|
|
4115
|
+
function rhythmLevel(beatInQuarters, meters) {
|
|
4116
|
+
const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, beatInQuarters, meters);
|
|
4117
|
+
const offset = beatInQuarters - _chunkS5BKZZGMcjs.barStartBeat.call(void 0, beatInQuarters, meters);
|
|
4118
|
+
const pulse = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts);
|
|
4119
|
+
if (isMultipleOf(offset, pulse)) {
|
|
4120
|
+
return _chunkS5BKZZGMcjs.metricWeight.call(void 0, beatInQuarters, meters) + 2;
|
|
4121
|
+
}
|
|
4122
|
+
const subUnit = pulse / (_chunkS5BKZZGMcjs.isCompound.call(void 0, ts) ? 3 : 2);
|
|
4123
|
+
return isMultipleOf(offset, subUnit) ? 1 : 0;
|
|
4124
|
+
}
|
|
4125
|
+
function ioiBinIndex(ioi) {
|
|
4126
|
+
const exponent = Math.min(4, Math.max(-4, Math.round(2 * Math.log2(ioi)) / 2));
|
|
4127
|
+
return Math.round((exponent + 4) / 0.5);
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
// src/analyze/rhythm/index.ts
|
|
4131
|
+
var RHYTHM_SLOTS_PER_BEAT = 12;
|
|
4132
|
+
var SLOT_WIDTH = 1 / RHYTHM_SLOTS_PER_BEAT;
|
|
4133
|
+
var RHYTHM_OFF_GRID_TOLERANCE = 1 / 32;
|
|
4134
|
+
var RHYTHM_IOI_BINS = Object.freeze(
|
|
4135
|
+
Array.from({ length: 17 }, (_, i) => 2 ** (-4 + i * 0.5))
|
|
4136
|
+
);
|
|
4137
|
+
function emptyAnalysis() {
|
|
4138
|
+
return {
|
|
4139
|
+
startBeat: 0,
|
|
4140
|
+
endBeat: 0,
|
|
4141
|
+
bars: 0,
|
|
4142
|
+
onsets: 0,
|
|
4143
|
+
onsetDensity: 0,
|
|
4144
|
+
barOnsets: [],
|
|
4145
|
+
onsetLevels: new Array(6).fill(0),
|
|
4146
|
+
barPositions: [],
|
|
4147
|
+
interOnsetShares: new Array(RHYTHM_IOI_BINS.length).fill(0),
|
|
4148
|
+
restRatio: 0,
|
|
4149
|
+
syncopation: 0,
|
|
4150
|
+
offGridRatio: 0,
|
|
4151
|
+
rationale: "Nothing sounds, so there is no rhythm to place on a grid"
|
|
4152
|
+
};
|
|
4153
|
+
}
|
|
4154
|
+
function foldOnsets(sounding) {
|
|
4155
|
+
const sorted = [...sounding].sort((a, b) => a.startBeat - b.startBeat);
|
|
4156
|
+
const onsets = [];
|
|
4157
|
+
for (const note of sorted) {
|
|
4158
|
+
const noteEnd = note.startBeat + note.durationBeat;
|
|
4159
|
+
const current = onsets[onsets.length - 1];
|
|
4160
|
+
if (current !== void 0 && note.startBeat - current.beat <= HUMANIZE_ADJACENCY) {
|
|
4161
|
+
current.end = Math.max(current.end, noteEnd);
|
|
4162
|
+
} else {
|
|
4163
|
+
onsets.push({ beat: note.startBeat, end: noteEnd });
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
return onsets;
|
|
4167
|
+
}
|
|
4168
|
+
function snapOnset(onset, meters) {
|
|
4169
|
+
const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, onset.beat, meters);
|
|
4170
|
+
const barStart = _chunkS5BKZZGMcjs.barStartBeat.call(void 0, onset.beat, meters);
|
|
4171
|
+
const offset = onset.beat - barStart;
|
|
4172
|
+
const slot = Math.round(offset / SLOT_WIDTH);
|
|
4173
|
+
const snapped = barStart + slot * SLOT_WIDTH;
|
|
4174
|
+
const offGrid = Math.abs(onset.beat - snapped) > RHYTHM_OFF_GRID_TOLERANCE;
|
|
4175
|
+
return { ...onset, ts, barStart, slot, snapped, level: rhythmLevel(snapped, meters), offGrid };
|
|
4176
|
+
}
|
|
4177
|
+
function tsKey(ts) {
|
|
4178
|
+
return ts.grouping === void 0 ? `${ts.numerator}/${ts.denominator}` : `${ts.numerator}/${ts.denominator}:${ts.grouping.join("+")}`;
|
|
4179
|
+
}
|
|
4180
|
+
function barGridBeats(bar, meters) {
|
|
4181
|
+
const barStart = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
|
|
4182
|
+
const ts = _chunkS5BKZZGMcjs.meterAt.call(void 0, barStart, meters);
|
|
4183
|
+
const barLength = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, barStart, meters);
|
|
4184
|
+
const subUnit = _chunkS5BKZZGMcjs.pulseBeats.call(void 0, ts) / (_chunkS5BKZZGMcjs.isCompound.call(void 0, ts) ? 3 : 2);
|
|
4185
|
+
const steps = Math.max(1, Math.round(barLength / subUnit));
|
|
4186
|
+
return Array.from({ length: steps }, (_, k) => barStart + k * subUnit);
|
|
4187
|
+
}
|
|
4188
|
+
function restRatioOf(sounding, windowStart, windowEnd) {
|
|
4189
|
+
const span = windowEnd - windowStart;
|
|
4190
|
+
if (span <= 0) {
|
|
4191
|
+
return 0;
|
|
4192
|
+
}
|
|
4193
|
+
const intervals = sounding.map((note) => [
|
|
4194
|
+
Math.max(windowStart, note.startBeat),
|
|
4195
|
+
Math.min(windowEnd, note.startBeat + note.durationBeat)
|
|
4196
|
+
]).filter(([from, to]) => to > from).sort((a, b) => a[0] - b[0]);
|
|
4197
|
+
let covered = 0;
|
|
4198
|
+
let curStart = 0;
|
|
4199
|
+
let curEnd = Number.NEGATIVE_INFINITY;
|
|
4200
|
+
for (const [from, to] of intervals) {
|
|
4201
|
+
if (from - curEnd > HUMANIZE_ADJACENCY) {
|
|
4202
|
+
if (curEnd > Number.NEGATIVE_INFINITY) {
|
|
4203
|
+
covered += curEnd - curStart;
|
|
4204
|
+
}
|
|
4205
|
+
curStart = from;
|
|
4206
|
+
curEnd = to;
|
|
4207
|
+
} else {
|
|
4208
|
+
curEnd = Math.max(curEnd, to);
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
if (curEnd > Number.NEGATIVE_INFINITY) {
|
|
4212
|
+
covered += curEnd - curStart;
|
|
4213
|
+
}
|
|
4214
|
+
return 1 - covered / span;
|
|
4215
|
+
}
|
|
4216
|
+
function syncopationOf(snapped, startBeat, endBeat, meters) {
|
|
4217
|
+
if (snapped.length === 0) {
|
|
4218
|
+
return 0;
|
|
4219
|
+
}
|
|
4220
|
+
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meters);
|
|
4221
|
+
const lastBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, endBeat, meters);
|
|
4222
|
+
const points = [];
|
|
4223
|
+
for (let bar = firstBar; bar <= lastBar; bar += 1) {
|
|
4224
|
+
for (const beat of barGridBeats(bar, meters)) {
|
|
4225
|
+
if (beat > startBeat + _chunkS5BKZZGMcjs.BEAT_EPS && beat <= endBeat + _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
4226
|
+
points.push({ beat, level: rhythmLevel(beat, meters) });
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
const normalizer = points.reduce((sum, p) => sum + p.level, 0);
|
|
4231
|
+
if (normalizer <= 0) {
|
|
4232
|
+
return 0;
|
|
4233
|
+
}
|
|
4234
|
+
let numerator = 0;
|
|
4235
|
+
let ptr = 0;
|
|
4236
|
+
for (let i = 0; i < snapped.length; i += 1) {
|
|
4237
|
+
const onset = snapped[i];
|
|
4238
|
+
if (onset === void 0) {
|
|
4239
|
+
continue;
|
|
4240
|
+
}
|
|
4241
|
+
const next = snapped[i + 1];
|
|
4242
|
+
const windowEnd = next !== void 0 ? next.snapped : onset.end;
|
|
4243
|
+
while (ptr < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _109 => _109[ptr], 'optionalAccess', _110 => _110.beat]), () => ( Number.POSITIVE_INFINITY))) <= onset.snapped + _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
4244
|
+
ptr += 1;
|
|
4245
|
+
}
|
|
4246
|
+
let scan = ptr;
|
|
4247
|
+
let maxLevel = 0;
|
|
4248
|
+
while (scan < points.length && (_nullishCoalesce(_optionalChain([points, 'access', _111 => _111[scan], 'optionalAccess', _112 => _112.beat]), () => ( Number.POSITIVE_INFINITY))) < windowEnd - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
4249
|
+
maxLevel = Math.max(maxLevel, _nullishCoalesce(_optionalChain([points, 'access', _113 => _113[scan], 'optionalAccess', _114 => _114.level]), () => ( 0)));
|
|
4250
|
+
scan += 1;
|
|
4251
|
+
}
|
|
4252
|
+
if (maxLevel > onset.level) {
|
|
4253
|
+
numerator += maxLevel - onset.level;
|
|
4254
|
+
}
|
|
4255
|
+
ptr = scan;
|
|
4256
|
+
}
|
|
4257
|
+
return numerator / normalizer;
|
|
4258
|
+
}
|
|
4259
|
+
function analyzeRhythm(notes, opts = {}) {
|
|
4260
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "rhythm meters");
|
|
4261
|
+
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "rhythm notes", { allowNonPositiveDuration: true, budget: opts.budget });
|
|
4262
|
+
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
4263
|
+
if (sounding.length === 0) {
|
|
4264
|
+
return emptyAnalysis();
|
|
4265
|
+
}
|
|
4266
|
+
const firstOnset = sounding.reduce(
|
|
4267
|
+
(min, n) => Math.min(min, n.startBeat),
|
|
4268
|
+
Number.POSITIVE_INFINITY
|
|
4269
|
+
);
|
|
4270
|
+
const startBeat = barGridStart(firstOnset, meters);
|
|
4271
|
+
const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
4272
|
+
const endBeat = Math.max(startBeat, _nullishCoalesce(opts.totalBeats, () => ( lastEnd)));
|
|
4273
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, endBeat, 0, Number.MAX_SAFE_INTEGER, "rhythm totalBeats");
|
|
4274
|
+
const bars = barSpanOf(startBeat, endBeat, meters);
|
|
4275
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, Math.ceil(bars) * 48, "rhythm analysis slots", opts.budget);
|
|
4276
|
+
const onsets = foldOnsets(sounding);
|
|
4277
|
+
const snapped = onsets.map((onset) => snapOnset(onset, meters));
|
|
4278
|
+
const onsetDensity = bars > 0 ? onsets.length / bars : 0;
|
|
4279
|
+
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meters);
|
|
4280
|
+
const barOnsets = new Array(lastBarOf(meters, startBeat, endBeat) - firstBar + 1).fill(0);
|
|
4281
|
+
for (const onset of onsets) {
|
|
4282
|
+
const index = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, onset.beat, meters) - firstBar;
|
|
4283
|
+
if (index >= 0 && index < barOnsets.length) {
|
|
4284
|
+
barOnsets[index] = (_nullishCoalesce(barOnsets[index], () => ( 0))) + 1;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
const onsetLevels = new Array(6).fill(0);
|
|
4288
|
+
let offGridCount = 0;
|
|
4289
|
+
for (const onset of snapped) {
|
|
4290
|
+
onsetLevels[onset.level] = (_nullishCoalesce(onsetLevels[onset.level], () => ( 0))) + 1;
|
|
4291
|
+
if (onset.offGrid) {
|
|
4292
|
+
offGridCount += 1;
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
for (let i = 0; i < onsetLevels.length; i += 1) {
|
|
4296
|
+
onsetLevels[i] = (_nullishCoalesce(onsetLevels[i], () => ( 0))) / onsets.length;
|
|
4297
|
+
}
|
|
4298
|
+
const profiles = [];
|
|
4299
|
+
const profileIndex = /* @__PURE__ */ new Map();
|
|
4300
|
+
for (const onset of snapped) {
|
|
4301
|
+
const key = tsKey(onset.ts);
|
|
4302
|
+
let index = profileIndex.get(key);
|
|
4303
|
+
if (index === void 0) {
|
|
4304
|
+
index = profiles.length;
|
|
4305
|
+
profileIndex.set(key, index);
|
|
4306
|
+
const slotsPerBar = Math.ceil(_chunkS5BKZZGMcjs.beatsPerBar.call(void 0, onset.ts) * RHYTHM_SLOTS_PER_BEAT);
|
|
4307
|
+
profiles.push({
|
|
4308
|
+
ts: onset.ts,
|
|
4309
|
+
slotsPerBar,
|
|
4310
|
+
shares: new Array(slotsPerBar).fill(0),
|
|
4311
|
+
onsets: 0
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
const profile = profiles[index];
|
|
4315
|
+
if (profile !== void 0) {
|
|
4316
|
+
const slotIndex = (onset.slot % profile.slotsPerBar + profile.slotsPerBar) % profile.slotsPerBar;
|
|
4317
|
+
profile.shares[slotIndex] = (_nullishCoalesce(profile.shares[slotIndex], () => ( 0))) + 1;
|
|
4318
|
+
profile.onsets += 1;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
for (const profile of profiles) {
|
|
4322
|
+
for (let i = 0; i < profile.shares.length; i += 1) {
|
|
4323
|
+
profile.shares[i] = (_nullishCoalesce(profile.shares[i], () => ( 0))) / profile.onsets;
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
const interOnsetShares = new Array(RHYTHM_IOI_BINS.length).fill(0);
|
|
4327
|
+
for (let i = 0; i < onsets.length; i += 1) {
|
|
4328
|
+
const onset = onsets[i];
|
|
4329
|
+
if (onset === void 0) {
|
|
4330
|
+
continue;
|
|
4331
|
+
}
|
|
4332
|
+
const next = onsets[i + 1];
|
|
4333
|
+
const ioi = next !== void 0 ? next.beat - onset.beat : onset.end - onset.beat;
|
|
4334
|
+
const bin = ioiBinIndex(ioi);
|
|
4335
|
+
interOnsetShares[bin] = (_nullishCoalesce(interOnsetShares[bin], () => ( 0))) + 1;
|
|
4336
|
+
}
|
|
4337
|
+
for (let i = 0; i < interOnsetShares.length; i += 1) {
|
|
4338
|
+
interOnsetShares[i] = (_nullishCoalesce(interOnsetShares[i], () => ( 0))) / onsets.length;
|
|
4339
|
+
}
|
|
4340
|
+
const restRatio = restRatioOf(sounding, startBeat, endBeat);
|
|
4341
|
+
const syncopation = syncopationOf(snapped, startBeat, endBeat, meters);
|
|
4342
|
+
const offGridRatio = offGridCount / onsets.length;
|
|
4343
|
+
return {
|
|
4344
|
+
startBeat,
|
|
4345
|
+
endBeat,
|
|
4346
|
+
bars,
|
|
4347
|
+
onsets: onsets.length,
|
|
4348
|
+
onsetDensity,
|
|
4349
|
+
barOnsets,
|
|
4350
|
+
onsetLevels,
|
|
4351
|
+
barPositions: profiles,
|
|
4352
|
+
interOnsetShares,
|
|
4353
|
+
restRatio,
|
|
4354
|
+
syncopation,
|
|
4355
|
+
offGridRatio,
|
|
4356
|
+
rationale: `${onsets.length} onsets over ${bars.toFixed(2)} bars (${onsetDensity.toFixed(2)} per bar), ${Math.round(restRatio * 100)}% rest, syncopation ${syncopation.toFixed(2)}`
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
// src/analyze/reference/types.ts
|
|
4361
|
+
var REFERENCE_PROFILE_VERSION = 1;
|
|
4362
|
+
|
|
4363
|
+
// src/analyze/reference/validate.ts
|
|
4364
|
+
var MASS_TOLERANCE = 1e-9;
|
|
4365
|
+
function assertString(value, name) {
|
|
4366
|
+
if (typeof value !== "string") {
|
|
4367
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be a string; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, value)}`);
|
|
4368
|
+
}
|
|
4369
|
+
return value;
|
|
4370
|
+
}
|
|
4371
|
+
function assertSpanOrder(startBeat, endBeat, name) {
|
|
4372
|
+
if (endBeat < startBeat) {
|
|
4373
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4374
|
+
`${name}.endBeat must be at least ${name}.startBeat; received ${endBeat} < ${startBeat}`
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
function assertNullable(value, name, validate) {
|
|
4379
|
+
if (value === void 0) {
|
|
4380
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be present; received undefined`);
|
|
4381
|
+
}
|
|
4382
|
+
return value === null ? null : validate(value, name);
|
|
4383
|
+
}
|
|
4384
|
+
function assertMassDistribution(value, length, name, allowAllZero) {
|
|
4385
|
+
const shares = _chunkS5BKZZGMcjs.assertArray.call(void 0, value, name);
|
|
4386
|
+
if (shares.length !== length) {
|
|
4387
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must have length ${length}; received ${shares.length}`);
|
|
4388
|
+
}
|
|
4389
|
+
let sum = 0;
|
|
4390
|
+
for (let index = 0; index < shares.length; index += 1) {
|
|
4391
|
+
const share = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, shares[index], `${name}[${index}]`);
|
|
4392
|
+
if (share < 0) {
|
|
4393
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}[${index}] must be at least 0; received ${share}`);
|
|
4394
|
+
}
|
|
4395
|
+
sum += share;
|
|
4396
|
+
}
|
|
4397
|
+
if (allowAllZero && sum === 0) {
|
|
4398
|
+
return shares;
|
|
4399
|
+
}
|
|
4400
|
+
if (Math.abs(sum - 1) > MASS_TOLERANCE) {
|
|
4401
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must sum to 1; received ${sum}`);
|
|
4402
|
+
}
|
|
4403
|
+
return shares;
|
|
4404
|
+
}
|
|
4405
|
+
function assertBarPositionProfile(value, name) {
|
|
4406
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4407
|
+
_chunkS5BKZZGMcjs.assertTimeSignature.call(void 0, read.ts, `${name}.ts`);
|
|
4408
|
+
const slotsPerBar = _chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, read.slotsPerBar, `${name}.slotsPerBar`);
|
|
4409
|
+
const onsets = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
|
|
4410
|
+
assertMassDistribution(read.shares, slotsPerBar, `${name}.shares`, onsets === 0);
|
|
4411
|
+
return read;
|
|
4412
|
+
}
|
|
4413
|
+
function assertRhythmAnalysis(value, name) {
|
|
4414
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4415
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4416
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4417
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4418
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
|
|
4419
|
+
const onsets = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.onsets, `${name}.onsets`, 0);
|
|
4420
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
|
|
4421
|
+
const barOnsets = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.barOnsets, `${name}.barOnsets`);
|
|
4422
|
+
for (let index = 0; index < barOnsets.length; index += 1) {
|
|
4423
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0,
|
|
4424
|
+
barOnsets[index],
|
|
4425
|
+
0,
|
|
4426
|
+
Number.MAX_SAFE_INTEGER,
|
|
4427
|
+
`${name}.barOnsets[${index}]`
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
assertMassDistribution(read.onsetLevels, 6, `${name}.onsetLevels`, onsets === 0);
|
|
4431
|
+
const barPositions = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.barPositions, `${name}.barPositions`);
|
|
4432
|
+
for (let index = 0; index < barPositions.length; index += 1) {
|
|
4433
|
+
assertBarPositionProfile(barPositions[index], `${name}.barPositions[${index}]`);
|
|
4434
|
+
}
|
|
4435
|
+
assertMassDistribution(
|
|
4436
|
+
read.interOnsetShares,
|
|
4437
|
+
RHYTHM_IOI_BINS.length,
|
|
4438
|
+
`${name}.interOnsetShares`,
|
|
4439
|
+
onsets === 0
|
|
4440
|
+
);
|
|
4441
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
|
|
4442
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.syncopation, 0, 1, `${name}.syncopation`);
|
|
4443
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.offGridRatio, 0, 1, `${name}.offGridRatio`);
|
|
4444
|
+
assertString(read.rationale, `${name}.rationale`);
|
|
4445
|
+
return read;
|
|
4446
|
+
}
|
|
4447
|
+
function assertFormSection(value, name, sectionCount) {
|
|
4448
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4449
|
+
assertString(read.label, `${name}.label`);
|
|
4450
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4451
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4452
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4453
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
|
|
4454
|
+
_chunkS5BKZZGMcjs.assertInteger.call(void 0, read.firstOccurrence, `${name}.firstOccurrence`, 0, Math.max(sectionCount - 1, 0));
|
|
4455
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
|
|
4456
|
+
assertString(read.rationale, `${name}.rationale`);
|
|
4457
|
+
return read;
|
|
4458
|
+
}
|
|
4459
|
+
function assertReferenceCadence(value, name) {
|
|
4460
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4461
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.atBeat, `${name}.atBeat`);
|
|
4462
|
+
_chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.type, CADENCE_TYPES, `${name}.type`);
|
|
4463
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.weight, 0, 1, `${name}.weight`);
|
|
4464
|
+
return read;
|
|
4465
|
+
}
|
|
4466
|
+
function assertReferencePhraseMelody(value, name) {
|
|
4467
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4468
|
+
_chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.shape, MELODIC_CONTOUR_SHAPES, `${name}.shape`);
|
|
4469
|
+
_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, read.low, `${name}.low`);
|
|
4470
|
+
_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, read.high, `${name}.high`);
|
|
4471
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.mean, 0, 127, `${name}.mean`);
|
|
4472
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.peakPitch, 0, 127, `${name}.peakPitch`);
|
|
4473
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.peakPosition, 0, 1, `${name}.peakPosition`);
|
|
4474
|
+
const outline = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.outline, `${name}.outline`);
|
|
4475
|
+
if (outline.length !== 8) {
|
|
4476
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.outline must have length 8; received ${outline.length}`);
|
|
4477
|
+
}
|
|
4478
|
+
for (let index = 0; index < outline.length; index += 1) {
|
|
4479
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, outline[index], `${name}.outline[${index}]`);
|
|
4480
|
+
}
|
|
4481
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.onsetDensity, 0, Number.MAX_SAFE_INTEGER, `${name}.onsetDensity`);
|
|
4482
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.restRatio, 0, 1, `${name}.restRatio`);
|
|
4483
|
+
return read;
|
|
4484
|
+
}
|
|
4485
|
+
function assertReferencePhrase(value, name, sectionCount, nodeCount) {
|
|
4486
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4487
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4488
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4489
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4490
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.bars`);
|
|
4491
|
+
assertNullable(
|
|
4492
|
+
read.section,
|
|
4493
|
+
`${name}.section`,
|
|
4494
|
+
(v, n) => _chunkS5BKZZGMcjs.assertInteger.call(void 0, v, n, 0, Math.max(sectionCount - 1, 0))
|
|
4495
|
+
);
|
|
4496
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
|
|
4497
|
+
assertNullable(read.cadence, `${name}.cadence`, assertReferenceCadence);
|
|
4498
|
+
assertNullable(read.melody, `${name}.melody`, assertReferencePhraseMelody);
|
|
4499
|
+
const motifNodes = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.motifNodes, `${name}.motifNodes`);
|
|
4500
|
+
for (let index = 0; index < motifNodes.length; index += 1) {
|
|
4501
|
+
_chunkS5BKZZGMcjs.assertInteger.call(void 0,
|
|
4502
|
+
motifNodes[index],
|
|
4503
|
+
`${name}.motifNodes[${index}]`,
|
|
4504
|
+
0,
|
|
4505
|
+
Math.max(nodeCount - 1, 0)
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
return read;
|
|
4509
|
+
}
|
|
4510
|
+
function assertReferenceForm(value, name, nodeCount) {
|
|
4511
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4512
|
+
const hypermeter = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
|
|
4513
|
+
read.hypermeter,
|
|
4514
|
+
`${name}.hypermeter`
|
|
4515
|
+
);
|
|
4516
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, hypermeter.groupBars, 0, Number.MAX_SAFE_INTEGER, `${name}.hypermeter.groupBars`);
|
|
4517
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, hypermeter.confidence, 0, 1, `${name}.hypermeter.confidence`);
|
|
4518
|
+
const sections = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.sections, `${name}.sections`);
|
|
4519
|
+
for (let index = 0; index < sections.length; index += 1) {
|
|
4520
|
+
assertFormSection(sections[index], `${name}.sections[${index}]`, sections.length);
|
|
4521
|
+
}
|
|
4522
|
+
const phrases = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.phrases, `${name}.phrases`);
|
|
4523
|
+
for (let index = 0; index < phrases.length; index += 1) {
|
|
4524
|
+
assertReferencePhrase(phrases[index], `${name}.phrases[${index}]`, sections.length, nodeCount);
|
|
4525
|
+
}
|
|
4526
|
+
return read;
|
|
4527
|
+
}
|
|
4528
|
+
function assertResolvedKeyField(value, name) {
|
|
4529
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4530
|
+
try {
|
|
4531
|
+
_chunk3R3QVKY5cjs.resolveKey.call(void 0, read);
|
|
4532
|
+
} catch (error) {
|
|
4533
|
+
if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
|
|
4534
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name} must be a valid key: ${error.message}`);
|
|
4535
|
+
}
|
|
4536
|
+
throw error;
|
|
4537
|
+
}
|
|
4538
|
+
return read;
|
|
4539
|
+
}
|
|
4540
|
+
function assertReferenceKeyRegion(value, name) {
|
|
4541
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4542
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4543
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4544
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4545
|
+
assertResolvedKeyField(read.key, `${name}.key`);
|
|
4546
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.confidence, 0, 1, `${name}.confidence`);
|
|
4547
|
+
return read;
|
|
4548
|
+
}
|
|
4549
|
+
function assertReferenceChord(value, name, keys) {
|
|
4550
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4551
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4552
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4553
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4554
|
+
const keyIndex = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.key, `${name}.key`, 0, Math.max(keys.length - 1, 0));
|
|
4555
|
+
const roman = assertString(read.roman, `${name}.roman`);
|
|
4556
|
+
try {
|
|
4557
|
+
romanToChord(roman, keys[keyIndex]);
|
|
4558
|
+
} catch (error) {
|
|
4559
|
+
if (error instanceof _chunkS5BKZZGMcjs.InvalidInputError) {
|
|
4560
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4561
|
+
`${name}.roman must be readable by romanToChord; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, roman)}: ${error.message}`
|
|
4562
|
+
);
|
|
4563
|
+
}
|
|
4564
|
+
throw error;
|
|
4565
|
+
}
|
|
4566
|
+
_chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.function, HARMONIC_FUNCTIONS, `${name}.function`);
|
|
4567
|
+
_chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.level, REDUCTION_LEVELS, `${name}.level`);
|
|
4568
|
+
return read;
|
|
4569
|
+
}
|
|
4570
|
+
function assertReferenceHarmony(value, name) {
|
|
4571
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4572
|
+
const keys = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.keys, `${name}.keys`);
|
|
4573
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
4574
|
+
assertReferenceKeyRegion(keys[index], `${name}.keys[${index}]`);
|
|
4575
|
+
}
|
|
4576
|
+
const resolvedKeys = keys.map((region) => region.key);
|
|
4577
|
+
const chords = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.chords, `${name}.chords`);
|
|
4578
|
+
for (let index = 0; index < chords.length; index += 1) {
|
|
4579
|
+
assertReferenceChord(chords[index], `${name}.chords[${index}]`, resolvedKeys);
|
|
4580
|
+
}
|
|
4581
|
+
assertRhythmAnalysis(read.rhythm, `${name}.rhythm`);
|
|
4582
|
+
return read;
|
|
4583
|
+
}
|
|
4584
|
+
function assertMotifRelationSummary(value, name) {
|
|
4585
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4586
|
+
_chunkS5BKZZGMcjs.assertOneOf.call(void 0, read.kind, MOTIF_RELATION_KINDS, `${name}.kind`);
|
|
4587
|
+
_chunkS5BKZZGMcjs.assertFlag.call(void 0, read.sequence, `${name}.sequence`);
|
|
4588
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.semitones, `${name}.semitones`);
|
|
4589
|
+
if (read.degrees !== void 0) {
|
|
4590
|
+
_chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.degrees, `${name}.degrees`);
|
|
4591
|
+
}
|
|
4592
|
+
const timeRatio = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.timeRatio, `${name}.timeRatio`);
|
|
4593
|
+
if (timeRatio <= 0) {
|
|
4594
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.timeRatio must be positive; received ${timeRatio}`);
|
|
4595
|
+
}
|
|
4596
|
+
return read;
|
|
4597
|
+
}
|
|
4598
|
+
function assertMotifGraphNode(value, name, occurrenceCounts) {
|
|
4599
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4600
|
+
const motif = _chunkS5BKZZGMcjs.assertInteger.call(void 0,
|
|
4601
|
+
read.motif,
|
|
4602
|
+
`${name}.motif`,
|
|
4603
|
+
0,
|
|
4604
|
+
Math.max(occurrenceCounts.length - 1, 0)
|
|
4605
|
+
);
|
|
4606
|
+
_chunkS5BKZZGMcjs.assertInteger.call(void 0,
|
|
4607
|
+
read.occurrence,
|
|
4608
|
+
`${name}.occurrence`,
|
|
4609
|
+
0,
|
|
4610
|
+
Math.max((_nullishCoalesce(occurrenceCounts[motif], () => ( 1))) - 1, 0)
|
|
4611
|
+
);
|
|
4612
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.startBeat, `${name}.startBeat`);
|
|
4613
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, read.endBeat, `${name}.endBeat`);
|
|
4614
|
+
assertSpanOrder(startBeat, endBeat, name);
|
|
4615
|
+
return read;
|
|
4616
|
+
}
|
|
4617
|
+
function assertMotifGraphEdge(value, name, nodeCount) {
|
|
4618
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4619
|
+
const from = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.from, `${name}.from`, 0, Math.max(nodeCount - 1, 0));
|
|
4620
|
+
const to = _chunkS5BKZZGMcjs.assertInteger.call(void 0, read.to, `${name}.to`, 0, Math.max(nodeCount - 1, 0));
|
|
4621
|
+
if (from >= to) {
|
|
4622
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4623
|
+
`${name}.from must be less than ${name}.to; received from=${from}, to=${to}`
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
assertNullable(read.relation, `${name}.relation`, assertMotifRelationSummary);
|
|
4627
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.similarity, 0, 1, `${name}.similarity`);
|
|
4628
|
+
return read;
|
|
4629
|
+
}
|
|
4630
|
+
function assertMotifGraph(value, name, occurrenceCounts) {
|
|
4631
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4632
|
+
const nodes = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.nodes, `${name}.nodes`);
|
|
4633
|
+
for (let index = 0; index < nodes.length; index += 1) {
|
|
4634
|
+
assertMotifGraphNode(nodes[index], `${name}.nodes[${index}]`, occurrenceCounts);
|
|
4635
|
+
}
|
|
4636
|
+
const edges = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.edges, `${name}.edges`);
|
|
4637
|
+
const seenTo = /* @__PURE__ */ new Set();
|
|
4638
|
+
for (let index = 0; index < edges.length; index += 1) {
|
|
4639
|
+
const edge = assertMotifGraphEdge(edges[index], `${name}.edges[${index}]`, nodes.length);
|
|
4640
|
+
if (seenTo.has(edge.to)) {
|
|
4641
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.edges: node ${edge.to} has more than one incoming edge`);
|
|
4642
|
+
}
|
|
4643
|
+
seenTo.add(edge.to);
|
|
4644
|
+
}
|
|
4645
|
+
return read;
|
|
4646
|
+
}
|
|
4647
|
+
function assertReferenceMotif(value, name) {
|
|
4648
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4649
|
+
const intervals = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.intervals, `${name}.intervals`);
|
|
4650
|
+
let pitch = 0;
|
|
4651
|
+
let low = 0;
|
|
4652
|
+
let high = 0;
|
|
4653
|
+
for (let index = 0; index < intervals.length; index += 1) {
|
|
4654
|
+
const interval = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, intervals[index], `${name}.intervals[${index}]`);
|
|
4655
|
+
if (!Number.isInteger(interval)) {
|
|
4656
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4657
|
+
`${name}.intervals[${index}] must be a whole number of semitones; received ${interval}`
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
pitch += interval;
|
|
4661
|
+
low = Math.min(low, pitch);
|
|
4662
|
+
high = Math.max(high, pitch);
|
|
4663
|
+
}
|
|
4664
|
+
if (high - low > 127) {
|
|
4665
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4666
|
+
`${name}.intervals must span at most 127 semitones; received ${high - low}`
|
|
4667
|
+
);
|
|
4668
|
+
}
|
|
4669
|
+
const rhythm = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.rhythm, `${name}.rhythm`);
|
|
4670
|
+
if (rhythm.length !== intervals.length) {
|
|
4671
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4672
|
+
`${name}.rhythm must hold one ratio per interval (${intervals.length}); received ${rhythm.length}`
|
|
4673
|
+
);
|
|
4674
|
+
}
|
|
4675
|
+
for (let index = 0; index < rhythm.length; index += 1) {
|
|
4676
|
+
const ratio2 = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, rhythm[index], `${name}.rhythm[${index}]`);
|
|
4677
|
+
if (!(ratio2 > 0)) {
|
|
4678
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(`${name}.rhythm[${index}] must be positive; received ${ratio2}`);
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
const unitBeats = _chunkS5BKZZGMcjs.assertRange.call(void 0, read.unitBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.unitBeats`);
|
|
4682
|
+
if (unitBeats > 0 !== rhythm.length > 0) {
|
|
4683
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4684
|
+
`${name}.unitBeats must be positive exactly when rhythm is non-empty; received ${unitBeats} with ${rhythm.length} ratio(s)`
|
|
4685
|
+
);
|
|
4686
|
+
}
|
|
4687
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, read.spanBeats, 0, Number.MAX_SAFE_INTEGER, `${name}.spanBeats`);
|
|
4688
|
+
_chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, read.occurrences, `${name}.occurrences`);
|
|
4689
|
+
return read;
|
|
4690
|
+
}
|
|
4691
|
+
function assertReferenceMelody(value, name) {
|
|
4692
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4693
|
+
assertNullable(read.register, `${name}.register`, (v, n) => {
|
|
4694
|
+
const register = _chunkS5BKZZGMcjs.assertRecord.call(void 0, v, n);
|
|
4695
|
+
_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, register.low, `${n}.low`);
|
|
4696
|
+
_chunkS5BKZZGMcjs.assertMidiPitch.call(void 0, register.high, `${n}.high`);
|
|
4697
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, register.mean, 0, 127, `${n}.mean`);
|
|
4698
|
+
return register;
|
|
4699
|
+
});
|
|
4700
|
+
assertNullable(read.shape, `${name}.shape`, (v, n) => _chunkS5BKZZGMcjs.assertOneOf.call(void 0, v, MELODIC_CONTOUR_SHAPES, n));
|
|
4701
|
+
const motifs = _chunkS5BKZZGMcjs.assertArray.call(void 0, read.motifs, `${name}.motifs`);
|
|
4702
|
+
const occurrenceCounts = [];
|
|
4703
|
+
for (let index = 0; index < motifs.length; index += 1) {
|
|
4704
|
+
assertReferenceMotif(motifs[index], `${name}.motifs[${index}]`);
|
|
4705
|
+
occurrenceCounts.push(motifs[index].occurrences);
|
|
4706
|
+
}
|
|
4707
|
+
assertMotifGraph(read.graph, `${name}.graph`, occurrenceCounts);
|
|
4708
|
+
assertRhythmAnalysis(read.rhythm, `${name}.rhythm`);
|
|
4709
|
+
return read;
|
|
4710
|
+
}
|
|
4711
|
+
function assertReferenceProfile(value, name = "reference profile") {
|
|
4712
|
+
const read = _chunkS5BKZZGMcjs.assertRecord.call(void 0, value, name);
|
|
4713
|
+
if (read.profileVersion !== REFERENCE_PROFILE_VERSION) {
|
|
4714
|
+
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
4715
|
+
`${name}.profileVersion must be ${REFERENCE_PROFILE_VERSION}; received ${_chunkS5BKZZGMcjs.describeRejected.call(void 0, read.profileVersion)}`
|
|
4716
|
+
);
|
|
4717
|
+
}
|
|
4718
|
+
const span = _chunkS5BKZZGMcjs.assertRecord.call(void 0,
|
|
4719
|
+
read.span,
|
|
4720
|
+
`${name}.span`
|
|
4721
|
+
);
|
|
4722
|
+
const startBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, span.startBeat, `${name}.span.startBeat`);
|
|
4723
|
+
const endBeat = _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, span.endBeat, `${name}.span.endBeat`);
|
|
4724
|
+
assertSpanOrder(startBeat, endBeat, `${name}.span`);
|
|
4725
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, span.bars, 0, Number.MAX_SAFE_INTEGER, `${name}.span.bars`);
|
|
4726
|
+
_chunkS5BKZZGMcjs.assertMeterMap.call(void 0, read.meters, `${name}.meters`);
|
|
4727
|
+
const melody = assertReferenceMelody(read.melody, `${name}.melody`);
|
|
4728
|
+
assertReferenceForm(read.form, `${name}.form`, melody.graph.nodes.length);
|
|
4729
|
+
assertReferenceHarmony(read.harmony, `${name}.harmony`);
|
|
4730
|
+
return read;
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
// src/analyze/reference/measures.ts
|
|
4734
|
+
var PROGRESSION_GAP = 0.75;
|
|
4735
|
+
var SAME_DEGREE_COST = 0.25;
|
|
4736
|
+
var SAME_FUNCTION_COST = 0.5;
|
|
4737
|
+
var HALF_PHRYGIAN_COST = 0.5;
|
|
4738
|
+
var MODE_CHANGE_COST = 0.5;
|
|
4739
|
+
var SHAPE_MISMATCH_COST = 0.25;
|
|
4740
|
+
var REGISTER_SPAN = 24;
|
|
4741
|
+
var PEAK_POSITION_WEIGHT = 0.5;
|
|
4742
|
+
var FAMILY_SIZE_BINS = 5;
|
|
4743
|
+
var DEPTH_BINS = 4;
|
|
4744
|
+
var NAMED_CHORDS = ["Ger", "It", "Fr", "N"];
|
|
4745
|
+
var PITCH_PRESERVING = /* @__PURE__ */ new Set([
|
|
4746
|
+
"repetition",
|
|
4747
|
+
"augmentation",
|
|
4748
|
+
"diminution"
|
|
4749
|
+
]);
|
|
4750
|
+
var CELL_START_PITCH = 60;
|
|
4751
|
+
function whenBoth(hasA, hasB, measure) {
|
|
4752
|
+
if (!hasA && !hasB) {
|
|
4753
|
+
return null;
|
|
4754
|
+
}
|
|
4755
|
+
if (!hasA || !hasB) {
|
|
4756
|
+
return 0;
|
|
4757
|
+
}
|
|
4758
|
+
return measure();
|
|
4759
|
+
}
|
|
4760
|
+
function aligned(a, b, cost, gap = 1) {
|
|
4761
|
+
return distanceToSimilarity(gradedEditDistance(a, b, cost, gap), a.length, b.length);
|
|
4762
|
+
}
|
|
4763
|
+
function alignedSimilarity(a, b, cost, gap = 1) {
|
|
4764
|
+
return whenBoth(a.length > 0, b.length > 0, () => aligned(a, b, cost, gap));
|
|
4765
|
+
}
|
|
4766
|
+
function massOf(values) {
|
|
4767
|
+
let total = 0;
|
|
4768
|
+
for (const value of values) {
|
|
4769
|
+
total += value;
|
|
4770
|
+
}
|
|
4771
|
+
return total;
|
|
4772
|
+
}
|
|
4773
|
+
function intersection(a, b) {
|
|
4774
|
+
const massA = massOf(a);
|
|
4775
|
+
const massB = massOf(b);
|
|
4776
|
+
let shared2 = 0;
|
|
4777
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
4778
|
+
shared2 += Math.min((_nullishCoalesce(a[i], () => ( 0))) / massA, (_nullishCoalesce(b[i], () => ( 0))) / massB);
|
|
4779
|
+
}
|
|
4780
|
+
return Math.min(1, shared2);
|
|
4781
|
+
}
|
|
4782
|
+
function distributionSimilarity(a, b) {
|
|
4783
|
+
return whenBoth(massOf(a) > 0, massOf(b) > 0, () => intersection(a, b));
|
|
4784
|
+
}
|
|
4785
|
+
function ratio(a, b) {
|
|
4786
|
+
const larger = Math.max(a, b);
|
|
4787
|
+
return larger === 0 ? 1 : Math.min(a, b) / larger;
|
|
4788
|
+
}
|
|
4789
|
+
function sectionSequenceSimilarity(a, b) {
|
|
4790
|
+
return alignedSimilarity(a, b, (x, y) => x === y ? 0 : 1);
|
|
4791
|
+
}
|
|
4792
|
+
function phraseLengthSimilarity(a, b) {
|
|
4793
|
+
return alignedSimilarity(a, b, (x, y) => {
|
|
4794
|
+
const longer = Math.max(x, y);
|
|
4795
|
+
return longer === 0 ? 0 : Math.min(Math.abs(x - y) / longer, 1);
|
|
4796
|
+
});
|
|
4797
|
+
}
|
|
4798
|
+
function simpleDegreeToken(roman) {
|
|
4799
|
+
for (const name of NAMED_CHORDS) {
|
|
4800
|
+
if (roman.startsWith(name)) {
|
|
4801
|
+
return name;
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4804
|
+
const match = roman.match(/^([b#]*)([IV]+)/i);
|
|
4805
|
+
return match ? `${match[1]}${match[2].toUpperCase()}` : roman;
|
|
4806
|
+
}
|
|
4807
|
+
function romanDegreeToken(roman) {
|
|
4808
|
+
const slash = roman.indexOf("/");
|
|
4809
|
+
if (slash < 0) {
|
|
4810
|
+
return simpleDegreeToken(roman);
|
|
4811
|
+
}
|
|
4812
|
+
return `${simpleDegreeToken(roman.slice(0, slash))}/${simpleDegreeToken(roman.slice(slash + 1))}`;
|
|
4813
|
+
}
|
|
4814
|
+
function chordCost(x, y) {
|
|
4815
|
+
if (x.roman === y.roman) {
|
|
4816
|
+
return 0;
|
|
4817
|
+
}
|
|
4818
|
+
if (romanDegreeToken(x.roman) === romanDegreeToken(y.roman)) {
|
|
4819
|
+
return SAME_DEGREE_COST;
|
|
4820
|
+
}
|
|
4821
|
+
return x.function === y.function ? SAME_FUNCTION_COST : 1;
|
|
4822
|
+
}
|
|
4823
|
+
function progressionSimilarity(a, b, budget) {
|
|
4824
|
+
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, a.length * b.length, "reference progression alignment", budget);
|
|
4825
|
+
return alignedSimilarity(a, b, chordCost, PROGRESSION_GAP);
|
|
4826
|
+
}
|
|
4827
|
+
function functionMass(chords) {
|
|
4828
|
+
const mass = HARMONIC_FUNCTIONS.map(() => 0);
|
|
4829
|
+
for (const chord of chords) {
|
|
4830
|
+
const bin = HARMONIC_FUNCTIONS.indexOf(chord.function);
|
|
4831
|
+
mass[bin] = (_nullishCoalesce(mass[bin], () => ( 0))) + Math.max(0, chord.endBeat - chord.startBeat);
|
|
4832
|
+
}
|
|
4833
|
+
return mass;
|
|
4834
|
+
}
|
|
4835
|
+
function functionSimilarity(a, b) {
|
|
4836
|
+
return distributionSimilarity(functionMass(a), functionMass(b));
|
|
4837
|
+
}
|
|
4838
|
+
function cadenceSimilarity(a, b) {
|
|
4839
|
+
return alignedSimilarity(a, b, (x, y) => {
|
|
4840
|
+
if (x === y) {
|
|
4841
|
+
return 0;
|
|
4842
|
+
}
|
|
4843
|
+
const halfAndPhrygian = x === "half" && y === "phrygian" || x === "phrygian" && y === "half";
|
|
4844
|
+
return halfAndPhrygian ? HALF_PHRYGIAN_COST : 1;
|
|
4845
|
+
});
|
|
4846
|
+
}
|
|
4847
|
+
function keyTokens(keys) {
|
|
4848
|
+
const home = _nullishCoalesce(_optionalChain([keys, 'access', _115 => _115[0], 'optionalAccess', _116 => _116.scale, 'access', _117 => _117.rootPc]), () => ( 0));
|
|
4849
|
+
return keys.map((key) => ({
|
|
4850
|
+
distance: ((key.scale.rootPc - home) % 12 + 12) % 12,
|
|
4851
|
+
mask: key.scale.modeMask12
|
|
4852
|
+
}));
|
|
4853
|
+
}
|
|
4854
|
+
function keyPlanSimilarity(a, b) {
|
|
4855
|
+
return alignedSimilarity(keyTokens(a), keyTokens(b), (x, y) => {
|
|
4856
|
+
if (x.distance !== y.distance) {
|
|
4857
|
+
return 1;
|
|
4858
|
+
}
|
|
4859
|
+
return x.mask === y.mask ? 0 : MODE_CHANGE_COST;
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
function onsetSimilarity(a, b) {
|
|
4863
|
+
return whenBoth(a.onsets > 0, b.onsets > 0, () => {
|
|
4864
|
+
let weighted = 0;
|
|
4865
|
+
let weight = 0;
|
|
4866
|
+
for (const left of a.barPositions) {
|
|
4867
|
+
for (const right of b.barPositions) {
|
|
4868
|
+
const shared2 = left.ts.numerator === right.ts.numerator && left.ts.denominator === right.ts.denominator && left.onsets > 0 && right.onsets > 0;
|
|
4869
|
+
if (shared2) {
|
|
4870
|
+
const w = left.onsets + right.onsets;
|
|
4871
|
+
weighted += w * intersection(left.shares, right.shares);
|
|
4872
|
+
weight += w;
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
return weight > 0 ? weighted / weight : intersection(a.onsetLevels, b.onsetLevels);
|
|
4877
|
+
});
|
|
4878
|
+
}
|
|
4879
|
+
function densitySimilarity(a, b) {
|
|
4880
|
+
return whenBoth(a.onsets > 0, b.onsets > 0, () => ratio(a.onsetDensity, b.onsetDensity));
|
|
4881
|
+
}
|
|
4882
|
+
function durationSimilarity(a, b) {
|
|
4883
|
+
return whenBoth(
|
|
4884
|
+
a.onsets > 0,
|
|
4885
|
+
b.onsets > 0,
|
|
4886
|
+
() => intersection(a.interOnsetShares, b.interOnsetShares)
|
|
4887
|
+
);
|
|
4888
|
+
}
|
|
4889
|
+
function syncopationSimilarity(a, b) {
|
|
4890
|
+
return whenBoth(a.onsets > 0, b.onsets > 0, () => 1 - Math.abs(a.syncopation - b.syncopation));
|
|
4891
|
+
}
|
|
4892
|
+
function cellNotes(motif) {
|
|
4893
|
+
const notes = [];
|
|
4894
|
+
let low = 0;
|
|
4895
|
+
let high = 0;
|
|
4896
|
+
let offset = 0;
|
|
4897
|
+
for (const interval of motif.intervals) {
|
|
4898
|
+
offset += interval;
|
|
4899
|
+
low = Math.min(low, offset);
|
|
4900
|
+
high = Math.max(high, offset);
|
|
4901
|
+
}
|
|
4902
|
+
let pitch = Math.min(127 - high, Math.max(-low, CELL_START_PITCH));
|
|
4903
|
+
let beat = 0;
|
|
4904
|
+
for (let i = 0; i <= motif.intervals.length; i += 1) {
|
|
4905
|
+
const step = _nullishCoalesce(motif.rhythm[i], () => ( 1));
|
|
4906
|
+
notes.push({ pitch, startBeat: beat, durationBeat: i < motif.intervals.length ? step : 1 });
|
|
4907
|
+
pitch += _nullishCoalesce(motif.intervals[i], () => ( 0));
|
|
4908
|
+
beat += step;
|
|
4909
|
+
}
|
|
4910
|
+
return notes;
|
|
4911
|
+
}
|
|
4912
|
+
function bestMatchMean(from, to) {
|
|
4913
|
+
let weighted = 0;
|
|
4914
|
+
let weight = 0;
|
|
4915
|
+
for (const motif of from) {
|
|
4916
|
+
let best = 0;
|
|
4917
|
+
for (const other of to) {
|
|
4918
|
+
best = Math.max(best, compareMelodies(motif.cell, other.cell).pitchSimilarity);
|
|
4919
|
+
}
|
|
4920
|
+
weighted += motif.occurrences * best;
|
|
4921
|
+
weight += motif.occurrences;
|
|
4922
|
+
}
|
|
4923
|
+
return weighted / weight;
|
|
4924
|
+
}
|
|
4925
|
+
function surfaceSimilarity(a, b) {
|
|
4926
|
+
return whenBoth(a.length > 0, b.length > 0, () => {
|
|
4927
|
+
const left = a.map((motif) => ({ cell: cellNotes(motif), occurrences: motif.occurrences }));
|
|
4928
|
+
const right = b.map((motif) => ({ cell: cellNotes(motif), occurrences: motif.occurrences }));
|
|
4929
|
+
return (bestMatchMean(left, right) + bestMatchMean(right, left)) / 2;
|
|
4930
|
+
});
|
|
4931
|
+
}
|
|
4932
|
+
function coverageOf(graph, spanBeats) {
|
|
4933
|
+
if (!(spanBeats > 0) || graph.nodes.length === 0) {
|
|
4934
|
+
return 0;
|
|
4935
|
+
}
|
|
4936
|
+
const spans = graph.nodes.map((node) => [node.startBeat, node.endBeat]).sort((x, y) => x[0] - y[0] || x[1] - y[1]);
|
|
4937
|
+
let covered = 0;
|
|
4938
|
+
let [runStart, runEnd] = spans[0];
|
|
4939
|
+
for (const [start, end] of spans) {
|
|
4940
|
+
if (start > runEnd) {
|
|
4941
|
+
covered += runEnd - runStart;
|
|
4942
|
+
runStart = start;
|
|
4943
|
+
runEnd = end;
|
|
4944
|
+
} else {
|
|
4945
|
+
runEnd = Math.max(runEnd, end);
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
covered += runEnd - runStart;
|
|
4949
|
+
return Math.min(1, covered / spanBeats);
|
|
4950
|
+
}
|
|
4951
|
+
function bump(histogram, bin) {
|
|
4952
|
+
histogram[bin] = (_nullishCoalesce(histogram[bin], () => ( 0))) + 1;
|
|
4953
|
+
}
|
|
4954
|
+
function summarise({ graph, spanBeats }) {
|
|
4955
|
+
const kinds = new Array(MOTIF_RELATION_KINDS.length + 1).fill(0);
|
|
4956
|
+
const parentEdge = /* @__PURE__ */ new Map();
|
|
4957
|
+
for (const edge of graph.edges) {
|
|
4958
|
+
const kind = _optionalChain([edge, 'access', _118 => _118.relation, 'optionalAccess', _119 => _119.kind]);
|
|
4959
|
+
bump(
|
|
4960
|
+
kinds,
|
|
4961
|
+
kind === void 0 ? MOTIF_RELATION_KINDS.length : MOTIF_RELATION_KINDS.indexOf(kind)
|
|
4962
|
+
);
|
|
4963
|
+
parentEdge.set(edge.to, edge);
|
|
4964
|
+
}
|
|
4965
|
+
const depth = new Array(graph.nodes.length).fill(0);
|
|
4966
|
+
const root = graph.nodes.map((_, i) => i);
|
|
4967
|
+
for (let node = 0; node < graph.nodes.length; node += 1) {
|
|
4968
|
+
const edge = parentEdge.get(node);
|
|
4969
|
+
if (edge !== void 0) {
|
|
4970
|
+
const kind = _optionalChain([edge, 'access', _120 => _120.relation, 'optionalAccess', _121 => _121.kind]);
|
|
4971
|
+
const keepsPitch = kind !== void 0 && PITCH_PRESERVING.has(kind);
|
|
4972
|
+
depth[node] = (_nullishCoalesce(depth[edge.from], () => ( 0))) + (keepsPitch ? 0 : 1);
|
|
4973
|
+
root[node] = _nullishCoalesce(root[edge.from], () => ( edge.from));
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
const familySize = /* @__PURE__ */ new Map();
|
|
4977
|
+
for (const r of root) {
|
|
4978
|
+
familySize.set(r, (_nullishCoalesce(familySize.get(r), () => ( 0))) + 1);
|
|
4979
|
+
}
|
|
4980
|
+
const families = new Array(FAMILY_SIZE_BINS).fill(0);
|
|
4981
|
+
for (const size of familySize.values()) {
|
|
4982
|
+
bump(families, Math.min(size, FAMILY_SIZE_BINS) - 1);
|
|
4983
|
+
}
|
|
4984
|
+
const depths = new Array(DEPTH_BINS).fill(0);
|
|
4985
|
+
for (const d of depth) {
|
|
4986
|
+
bump(depths, Math.min(d, DEPTH_BINS - 1));
|
|
4987
|
+
}
|
|
4988
|
+
return { kinds, families, depths, coverage: coverageOf(graph, spanBeats) };
|
|
4989
|
+
}
|
|
4990
|
+
function motifStructureSimilarity(a, b) {
|
|
4991
|
+
const left = summarise(a);
|
|
4992
|
+
const right = summarise(b);
|
|
4993
|
+
const terms = [
|
|
4994
|
+
distributionSimilarity(left.kinds, right.kinds),
|
|
4995
|
+
distributionSimilarity(left.families, right.families),
|
|
4996
|
+
distributionSimilarity(left.depths, right.depths),
|
|
4997
|
+
whenBoth(
|
|
4998
|
+
left.coverage > 0,
|
|
4999
|
+
right.coverage > 0,
|
|
5000
|
+
() => 1 - Math.abs(left.coverage - right.coverage)
|
|
5001
|
+
)
|
|
5002
|
+
].filter((term) => term !== null);
|
|
5003
|
+
return terms.length === 0 ? null : massOf(terms) / terms.length;
|
|
5004
|
+
}
|
|
5005
|
+
function nullableCost(cost) {
|
|
5006
|
+
return (x, y) => {
|
|
5007
|
+
if (x === null || y === null) {
|
|
5008
|
+
return x === y ? 0 : 1;
|
|
5009
|
+
}
|
|
5010
|
+
return cost(x, y);
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
function phraseSimilarity(a, b, cost) {
|
|
5014
|
+
const hasA = a.some((phrase) => phrase !== null);
|
|
5015
|
+
const hasB = b.some((phrase) => phrase !== null);
|
|
5016
|
+
return whenBoth(hasA, hasB, () => aligned(a, b, nullableCost(cost)));
|
|
5017
|
+
}
|
|
5018
|
+
function normalisedOutline(phrase) {
|
|
5019
|
+
const mean = massOf(phrase.outline) / (phrase.outline.length || 1);
|
|
5020
|
+
const range = phrase.high - phrase.low || 1;
|
|
5021
|
+
return phrase.outline.map((point) => (point - mean) / range);
|
|
5022
|
+
}
|
|
5023
|
+
function contourCost(x, y) {
|
|
5024
|
+
const left = normalisedOutline(x);
|
|
5025
|
+
const right = normalisedOutline(y);
|
|
5026
|
+
let difference = 0;
|
|
5027
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
5028
|
+
difference += Math.abs((_nullishCoalesce(left[i], () => ( 0))) - (_nullishCoalesce(right[i], () => ( 0))));
|
|
5029
|
+
}
|
|
5030
|
+
const outlineCost = Math.min(1, difference / (left.length || 1));
|
|
5031
|
+
return Math.min(1, outlineCost + (x.shape === y.shape ? 0 : SHAPE_MISMATCH_COST));
|
|
5032
|
+
}
|
|
5033
|
+
function contourSimilarity(a, b) {
|
|
5034
|
+
return phraseSimilarity(a, b, contourCost);
|
|
5035
|
+
}
|
|
5036
|
+
function registerSimilarity(a, b) {
|
|
5037
|
+
const meanA = _nullishCoalesce(a.mean, () => ( 0));
|
|
5038
|
+
const meanB = _nullishCoalesce(b.mean, () => ( 0));
|
|
5039
|
+
return phraseSimilarity(a.phrases, b.phrases, (x, y) => {
|
|
5040
|
+
const offset = Math.abs(x.mean - meanA - (y.mean - meanB));
|
|
5041
|
+
const range = Math.abs(x.high - x.low - (y.high - y.low));
|
|
5042
|
+
const peak = Math.abs(x.peakPosition - y.peakPosition);
|
|
5043
|
+
return Math.min(1, (offset + range) / REGISTER_SPAN + PEAK_POSITION_WEIGHT * peak);
|
|
5044
|
+
});
|
|
5045
|
+
}
|
|
5046
|
+
|
|
5047
|
+
// src/analyze/reference/internal.ts
|
|
5048
|
+
function weightedMean(notes) {
|
|
5049
|
+
let weighted = 0;
|
|
5050
|
+
let total = 0;
|
|
5051
|
+
for (const note of notes) {
|
|
5052
|
+
weighted += note.pitch * note.durationBeat;
|
|
5053
|
+
total += note.durationBeat;
|
|
5054
|
+
}
|
|
5055
|
+
return total > 0 ? weighted / total : 0;
|
|
5056
|
+
}
|
|
5057
|
+
function phraseMelody(line, startBeat, endBeat, lineMean, meters, budget) {
|
|
5058
|
+
const inside = line.filter((note) => note.startBeat >= startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS).map((note) => ({
|
|
5059
|
+
...note,
|
|
5060
|
+
durationBeat: Math.min(note.startBeat + note.durationBeat, endBeat) - note.startBeat
|
|
5061
|
+
}));
|
|
5062
|
+
const first = inside[0];
|
|
5063
|
+
if (first === void 0 || inside.length < 2) {
|
|
5064
|
+
return null;
|
|
5065
|
+
}
|
|
5066
|
+
let low = first.pitch;
|
|
5067
|
+
let peak = first;
|
|
5068
|
+
for (const note of inside) {
|
|
5069
|
+
low = Math.min(low, note.pitch);
|
|
5070
|
+
if (note.pitch > peak.pitch) {
|
|
5071
|
+
peak = note;
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
const length = endBeat - startBeat;
|
|
5075
|
+
const outline = [];
|
|
5076
|
+
for (let k = 0; k < 8; k += 1) {
|
|
5077
|
+
const at = startBeat + (k + 0.5) * length / 8;
|
|
5078
|
+
let held = first;
|
|
5079
|
+
for (const note of inside) {
|
|
5080
|
+
if (note.startBeat > at + _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
5081
|
+
break;
|
|
5082
|
+
}
|
|
5083
|
+
held = note;
|
|
5084
|
+
}
|
|
5085
|
+
outline.push(held.pitch - lineMean);
|
|
5086
|
+
}
|
|
5087
|
+
const rhythm = analyzeRhythm(inside, { meters, totalBeats: endBeat, budget });
|
|
5088
|
+
return {
|
|
5089
|
+
shape: melodicContour(inside).shape,
|
|
5090
|
+
low,
|
|
5091
|
+
high: peak.pitch,
|
|
5092
|
+
mean: weightedMean(inside),
|
|
5093
|
+
peakPitch: peak.pitch,
|
|
5094
|
+
peakPosition: Math.min(1, Math.max(0, (peak.startBeat - startBeat) / length)),
|
|
5095
|
+
outline,
|
|
5096
|
+
onsetDensity: rhythm.onsetDensity,
|
|
5097
|
+
restRatio: rhythm.restRatio
|
|
5098
|
+
};
|
|
5099
|
+
}
|
|
5100
|
+
|
|
5101
|
+
|
|
5102
|
+
|
|
5103
|
+
|
|
5104
|
+
|
|
5105
|
+
|
|
5106
|
+
|
|
5107
|
+
|
|
5108
|
+
|
|
5109
|
+
|
|
5110
|
+
|
|
5111
|
+
|
|
5112
|
+
|
|
5113
|
+
|
|
5114
|
+
|
|
5115
|
+
|
|
5116
|
+
|
|
5117
|
+
|
|
5118
|
+
|
|
5119
|
+
|
|
5120
|
+
|
|
5121
|
+
|
|
5122
|
+
|
|
5123
|
+
|
|
5124
|
+
|
|
5125
|
+
|
|
5126
|
+
|
|
5127
|
+
|
|
5128
|
+
|
|
5129
|
+
|
|
5130
|
+
|
|
5131
|
+
|
|
5132
|
+
|
|
5133
|
+
|
|
5134
|
+
|
|
5135
|
+
|
|
3270
5136
|
|
|
3271
5137
|
|
|
3272
5138
|
|
|
@@ -3325,5 +5191,5 @@ function detectCadences(timeline, key) {
|
|
|
3325
5191
|
|
|
3326
5192
|
|
|
3327
5193
|
|
|
3328
|
-
exports.HUMANIZE_ADJACENCY = HUMANIZE_ADJACENCY; exports.sameInstant = sameInstant; exports.adjacent = adjacent; exports.hasEnded = hasEnded; exports.windowWeights = windowWeights; exports.MODAL_SCALE_NAMES = MODAL_SCALE_NAMES; exports.detectChord = detectChord; exports.detectChordBest = detectChordBest; exports.detectKey = detectKey; exports.detectKeyFromNotes = detectKeyFromNotes; exports.detectKeyBest = detectKeyBest; exports.augmentedSixthChord = augmentedSixthChord; exports.augmentedSixthKind = augmentedSixthKind; exports.augmentedSixthFromPitchClasses = augmentedSixthFromPitchClasses; exports.spellAugmentedSixth = spellAugmentedSixth; exports.
|
|
3329
|
-
//# sourceMappingURL=chunk-
|
|
5194
|
+
exports.HUMANIZE_ADJACENCY = HUMANIZE_ADJACENCY; exports.sameInstant = sameInstant; exports.adjacent = adjacent; exports.hasEnded = hasEnded; exports.windowWeights = windowWeights; exports.MODAL_SCALE_NAMES = MODAL_SCALE_NAMES; exports.detectChord = detectChord; exports.detectChordBest = detectChordBest; exports.detectKey = detectKey; exports.detectKeyFromNotes = detectKeyFromNotes; exports.detectKeyBest = detectKeyBest; exports.augmentedSixthChord = augmentedSixthChord; exports.augmentedSixthKind = augmentedSixthKind; exports.augmentedSixthFromPitchClasses = augmentedSixthFromPitchClasses; exports.spellAugmentedSixth = spellAugmentedSixth; exports.CADENCE_TYPES = CADENCE_TYPES; exports.detectCadence = detectCadence; exports.isBorrowedChord = isBorrowedChord; exports.borrowedSource = borrowedSource; exports.romanToChord = romanToChord; exports.chordToRoman = chordToRoman; exports.explainRoman = explainRoman; exports.HARMONIC_FUNCTIONS = HARMONIC_FUNCTIONS; exports.isMinorKey = isMinorKey; exports.functionOf = functionOf; exports.isDiatonic = isDiatonic; exports.parallelKey = parallelKey; exports.analyzeChord = analyzeChord; exports.secondaryDominant = secondaryDominant; exports.secondaryDominantOf = secondaryDominantOf; exports.pivotChords = pivotChords; exports.barSpanOf = barSpanOf; exports.clamp01 = clamp01; exports.floorMod = floorMod; exports.firstSoundingBeat = firstSoundingBeat; exports.lastBarOf = lastBarOf; exports.sliceBars = sliceBars; exports.weightSimilarity = weightSimilarity; exports.harmonicNovelty = harmonicNovelty; exports.combineEvidence = combineEvidence; exports.gridForNotes = gridForNotes; exports.barGridStart = barGridStart; exports.keyTimelineFromNotes = keyTimelineFromNotes; exports.detectModulations = detectModulations; exports.attachPivots = attachPivots; exports.keyLookup = keyLookup; exports.prevailingKeyOf = prevailingKeyOf; exports.toVoiceNotes = toVoiceNotes; exports.analyzeVoice = analyzeVoice; exports.assertChordTimeline = assertChordTimeline; exports.chordTimelineFromChords = chordTimelineFromChords; exports.chordAtBeat = chordAtBeat; exports.segmentStartingAt = segmentStartingAt; exports.chordTimelineFromNotes = chordTimelineFromNotes; exports.dirtySlotsFor = dirtySlotsFor; exports.analyzeTimeline = analyzeTimeline; exports.detectCadences = detectCadences; exports.orderedNotes = orderedNotes; exports.MELODIC_CONTOUR_SHAPES = MELODIC_CONTOUR_SHAPES; exports.melodicContour = melodicContour; exports.extractMotifs = extractMotifs; exports.motifFromNotes = motifFromNotes; exports.MOTIF_RELATION_KINDS = MOTIF_RELATION_KINDS; exports.relateMotifs = relateMotifs; exports.compareMelodies = compareMelodies; exports.melodicSimilarity = melodicSimilarity; exports.DEFAULT_VARIATION_THRESHOLD = DEFAULT_VARIATION_THRESHOLD; exports.motifGraph = motifGraph; exports.REDUCTION_LEVELS = REDUCTION_LEVELS; exports.reduceProgression = reduceProgression; exports.sectionSequenceSimilarity = sectionSequenceSimilarity; exports.phraseLengthSimilarity = phraseLengthSimilarity; exports.progressionSimilarity = progressionSimilarity; exports.functionSimilarity = functionSimilarity; exports.cadenceSimilarity = cadenceSimilarity; exports.keyPlanSimilarity = keyPlanSimilarity; exports.onsetSimilarity = onsetSimilarity; exports.densitySimilarity = densitySimilarity; exports.durationSimilarity = durationSimilarity; exports.syncopationSimilarity = syncopationSimilarity; exports.surfaceSimilarity = surfaceSimilarity; exports.motifStructureSimilarity = motifStructureSimilarity; exports.contourSimilarity = contourSimilarity; exports.registerSimilarity = registerSimilarity; exports.rhythmLevel = rhythmLevel; exports.ioiBinIndex = ioiBinIndex; exports.RHYTHM_IOI_BINS = RHYTHM_IOI_BINS; exports.analyzeRhythm = analyzeRhythm; exports.REFERENCE_PROFILE_VERSION = REFERENCE_PROFILE_VERSION; exports.assertReferenceProfile = assertReferenceProfile; exports.weightedMean = weightedMean; exports.phraseMelody = phraseMelody;
|
|
5195
|
+
//# sourceMappingURL=chunk-IFZDWIKM.cjs.map
|