@libraz/libcantus 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +63 -0
- package/README.md +7 -1
- package/README_ja.md +17 -11
- package/dist/analyze/index.cjs +31 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +161 -13
- package/dist/analyze/index.d.ts +161 -13
- package/dist/analyze/index.js +37 -13
- package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
- package/dist/chunk-2G5MCGYU.js.map +1 -0
- package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
- package/dist/chunk-3FDBMIPR.cjs.map +1 -0
- package/dist/{chunk-HPVSH7SF.cjs → chunk-3MDOEFIQ.cjs} +618 -954
- package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
- package/dist/{chunk-FBH75DAY.js → chunk-5MWRBYAT.js} +560 -159
- package/dist/chunk-5MWRBYAT.js.map +1 -0
- package/dist/{chunk-J7FHVZII.cjs → chunk-6TTBEON3.cjs} +3074 -740
- package/dist/chunk-6TTBEON3.cjs.map +1 -0
- package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
- package/dist/chunk-7NQ6AL3H.js.map +1 -0
- package/dist/{chunk-HWHGOVO6.cjs → chunk-E3QO3UET.cjs} +31 -23
- package/dist/chunk-E3QO3UET.cjs.map +1 -0
- package/dist/{chunk-H2KJHBW4.js → chunk-FJFULJZE.js} +2375 -191
- package/dist/chunk-FJFULJZE.js.map +1 -0
- package/dist/{chunk-Q6LFSAKV.js → chunk-FK3DGAIZ.js} +2657 -323
- package/dist/chunk-FK3DGAIZ.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -1
- package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
- package/dist/chunk-H7FVMBUC.js.map +1 -0
- package/dist/{chunk-DSMJRXCZ.cjs → chunk-HYSVSFNI.cjs} +931 -530
- package/dist/chunk-HYSVSFNI.cjs.map +1 -0
- package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
- package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
- package/dist/chunk-LQZQDBCD.cjs +5513 -0
- package/dist/chunk-LQZQDBCD.cjs.map +1 -0
- package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
- package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
- package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
- package/dist/chunk-S3QELJUE.cjs.map +1 -0
- package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
- package/dist/chunk-TXFWSU5D.js.map +1 -0
- package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
- package/dist/chunk-U4EQTDGN.cjs.map +1 -0
- package/dist/chunk-UGLFDG7L.cjs.map +1 -1
- package/dist/{chunk-HYJJ2V2I.js → chunk-UTE5A36R.js} +483 -819
- package/dist/chunk-UTE5A36R.js.map +1 -0
- package/dist/{chunk-NFJ4P6ZZ.js → chunk-UZKQISRF.js} +17 -9
- package/dist/chunk-UZKQISRF.js.map +1 -0
- package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
- package/dist/chunk-VL5NXVCN.js.map +1 -0
- package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
- package/dist/chunk-WOMCXN2W.cjs.map +1 -0
- package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
- package/dist/chunk-YDGMWH43.cjs.map +1 -0
- package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
- package/dist/chunk-YYVXRV3V.js.map +1 -0
- package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
- package/dist/chunk-ZLNSH4UL.js.map +1 -0
- package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
- package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
- package/dist/core/index.cjs +5 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +9 -8
- package/dist/core/index.d.ts +9 -8
- package/dist/core/index.js +4 -4
- package/dist/generate/index.cjs +20 -8
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +105 -17
- package/dist/generate/index.d.ts +105 -17
- package/dist/generate/index.js +19 -7
- package/dist/{index-CL4b4gUQ.d.ts → index-5VJgKz_Q.d.cts} +6 -2
- package/dist/{index-B9QLziDe.d.cts → index-BLuDesVG.d.ts} +20 -13
- package/dist/{index-BHIUyjhZ.d.ts → index-BV6APVa6.d.cts} +20 -13
- package/dist/{index-BMweUlm5.d.cts → index-Bf0Sv8a9.d.ts} +6 -2
- package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
- package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
- package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
- package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
- package/dist/{index-BNSM-DSk.d.cts → index-DVInzCDS.d.cts} +533 -28
- package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
- package/dist/{index-BJgP97N1.d.ts → index-jgeug9EA.d.ts} +533 -28
- package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
- package/dist/index.cjs +49 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.js +55 -19
- package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
- package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
- package/dist/model/index.cjs +12 -12
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +259 -48
- package/dist/model/index.d.ts +259 -48
- package/dist/model/index.js +11 -11
- package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
- package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
- package/dist/{index-BRMbPkHE.d.ts → profile-B4WqcM4H.d.ts} +85 -610
- package/dist/{index-DGCNKAUr.d.cts → profile-BJd1XkPB.d.cts} +85 -610
- package/dist/theory/index.cjs +5 -5
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +18 -14
- package/dist/theory/index.d.ts +18 -14
- package/dist/theory/index.js +4 -4
- package/dist/{index-CJBBiAmU.d.ts → types-7mGFwvMm.d.ts} +1210 -31
- package/dist/{index-BBWNZS0f.d.cts → types-_ofpb2be.d.cts} +1210 -31
- package/package.json +13 -13
- package/dist/chunk-35NBV2FT.cjs.map +0 -1
- package/dist/chunk-3R3QVKY5.cjs.map +0 -1
- package/dist/chunk-76ZBTRG2.cjs.map +0 -1
- package/dist/chunk-7KG3RTYS.js.map +0 -1
- package/dist/chunk-AGOVW7U6.js.map +0 -1
- package/dist/chunk-CGBADIO5.cjs.map +0 -1
- package/dist/chunk-DSMJRXCZ.cjs.map +0 -1
- package/dist/chunk-FBH75DAY.js.map +0 -1
- package/dist/chunk-H2KJHBW4.js.map +0 -1
- package/dist/chunk-HPVSH7SF.cjs.map +0 -1
- package/dist/chunk-HWHGOVO6.cjs.map +0 -1
- package/dist/chunk-HYJJ2V2I.js.map +0 -1
- package/dist/chunk-J7FHVZII.cjs.map +0 -1
- package/dist/chunk-M5RYMAFI.js.map +0 -1
- package/dist/chunk-NB25P3EH.js.map +0 -1
- package/dist/chunk-NFJ4P6ZZ.js.map +0 -1
- package/dist/chunk-NM7UAMC3.cjs.map +0 -1
- package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
- package/dist/chunk-PUKJP2Z5.js.map +0 -1
- package/dist/chunk-Q6LFSAKV.js.map +0 -1
- package/dist/chunk-RFIBF2RR.js.map +0 -1
- package/dist/chunk-S5BKZZGM.cjs.map +0 -1
- package/dist/chunk-SGLEQVJH.cjs +0 -3329
- package/dist/chunk-SGLEQVJH.cjs.map +0 -1
- package/dist/chunk-UMWZ4ZET.js.map +0 -1
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
var _chunkSGLEQVJHcjs = require('./chunk-SGLEQVJH.cjs');
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
|
|
@@ -41,7 +40,6 @@ var _chunkSGLEQVJHcjs = require('./chunk-SGLEQVJH.cjs');
|
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
|
|
44
|
-
var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
|
|
45
43
|
|
|
46
44
|
|
|
47
45
|
|
|
@@ -60,17 +58,42 @@ var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
|
|
|
60
58
|
|
|
61
59
|
|
|
62
60
|
|
|
61
|
+
var _chunkLQZQDBCDcjs = require('./chunk-LQZQDBCD.cjs');
|
|
63
62
|
|
|
64
63
|
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
var _chunkYDGMWH43cjs = require('./chunk-YDGMWH43.cjs');
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
var _chunkS3QELJUEcjs = require('./chunk-S3QELJUE.cjs');
|
|
69
92
|
|
|
70
93
|
// src/analyze/arrange/internal.ts
|
|
71
94
|
var MAX_LANE_LEAP = 24;
|
|
72
95
|
function covers(note, beat) {
|
|
73
|
-
return beat >= note.startBeat -
|
|
96
|
+
return beat >= note.startBeat - _chunkS3QELJUEcjs.BEAT_EPS && beat < note.endBeat - _chunkS3QELJUEcjs.BEAT_EPS;
|
|
74
97
|
}
|
|
75
98
|
function isPercussion(role) {
|
|
76
99
|
return role === "drums";
|
|
@@ -84,31 +107,31 @@ var TRACK_ROLE_TABLE = {
|
|
|
84
107
|
};
|
|
85
108
|
var TRACK_ROLES = Object.keys(TRACK_ROLE_TABLE);
|
|
86
109
|
function trackRoleOf(role, name) {
|
|
87
|
-
return role === void 0 ? "other" :
|
|
110
|
+
return role === void 0 ? "other" : _chunkS3QELJUEcjs.assertOneOf.call(void 0, role, TRACK_ROLES, name);
|
|
88
111
|
}
|
|
89
112
|
var DEFAULT_PROFILE = "pop";
|
|
90
|
-
var SAFETY_PROFILES = Object.keys(
|
|
113
|
+
var SAFETY_PROFILES = Object.keys(_chunkYDGMWH43cjs.PROFILE_WEIGHTS);
|
|
91
114
|
function arrangementProfile(profile) {
|
|
92
|
-
return
|
|
115
|
+
return _chunkS3QELJUEcjs.assertOneOf.call(void 0, _nullishCoalesce(profile, () => ( DEFAULT_PROFILE)), SAFETY_PROFILES, "arrangement profile");
|
|
93
116
|
}
|
|
94
117
|
function assertTrackNotes(tracks, options) {
|
|
95
|
-
|
|
118
|
+
_chunkS3QELJUEcjs.assertArray.call(void 0, tracks, "arrangement tracks");
|
|
96
119
|
let noteCount = 0;
|
|
97
120
|
for (let index = 0; index < tracks.length; index += 1) {
|
|
98
121
|
const notes = _optionalChain([tracks, 'access', _2 => _2[index], 'optionalAccess', _3 => _3.notes]);
|
|
99
122
|
if (!Array.isArray(notes)) {
|
|
100
|
-
throw new (0,
|
|
101
|
-
`tracks[${index}].notes must be an array; received ${
|
|
123
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
124
|
+
`tracks[${index}].notes must be an array; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, notes)}`
|
|
102
125
|
);
|
|
103
126
|
}
|
|
104
|
-
|
|
127
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, `tracks[${index}].notes`, options);
|
|
105
128
|
noteCount += notes.length;
|
|
106
129
|
}
|
|
107
130
|
return noteCount;
|
|
108
131
|
}
|
|
109
132
|
function assertGivenTimeline(timeline, name) {
|
|
110
133
|
if (timeline !== void 0) {
|
|
111
|
-
|
|
134
|
+
_chunkLQZQDBCDcjs.assertChordTimeline.call(void 0, timeline, name);
|
|
112
135
|
}
|
|
113
136
|
}
|
|
114
137
|
function assertGivenKeys(keys, name) {
|
|
@@ -116,26 +139,27 @@ function assertGivenKeys(keys, name) {
|
|
|
116
139
|
return;
|
|
117
140
|
}
|
|
118
141
|
if (!Array.isArray(keys)) {
|
|
119
|
-
throw new (0,
|
|
120
|
-
`${name} must be an array of key regions; received ${
|
|
142
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
143
|
+
`${name} must be an array of key regions; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, keys)}`
|
|
121
144
|
);
|
|
122
145
|
}
|
|
123
146
|
for (let index = 0; index < keys.length; index += 1) {
|
|
124
147
|
const region = keys[index];
|
|
125
148
|
if (typeof region !== "object" || region === null) {
|
|
126
|
-
throw new (0,
|
|
127
|
-
`${name}[${index}] must be a key region; received ${
|
|
149
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
150
|
+
`${name}[${index}] must be a key region; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, region)}`
|
|
128
151
|
);
|
|
129
152
|
}
|
|
130
|
-
|
|
131
|
-
|
|
153
|
+
_chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, region.startBeat, `${name}[${index}].startBeat`);
|
|
154
|
+
_chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, region.endBeat, `${name}[${index}].endBeat`);
|
|
132
155
|
if (region.key === void 0 || region.key === null || typeof region.key !== "string" && typeof region.key !== "object") {
|
|
133
|
-
throw new (0,
|
|
134
|
-
`${name}[${index}].key must name a key; received ${
|
|
156
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
157
|
+
`${name}[${index}].key must name a key; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, region.key)}`
|
|
135
158
|
);
|
|
136
159
|
}
|
|
137
|
-
|
|
160
|
+
_chunkYDGMWH43cjs.resolveKey.call(void 0, region.key);
|
|
138
161
|
}
|
|
162
|
+
_chunkLQZQDBCDcjs.assertSpansInOrder.call(void 0, keys, name);
|
|
139
163
|
}
|
|
140
164
|
function harmonyTrackSet(harmonyTracks, tracks, name = "harmonyTracks") {
|
|
141
165
|
if (harmonyTracks === void 0) {
|
|
@@ -145,16 +169,16 @@ function harmonyTrackSet(harmonyTracks, tracks, name = "harmonyTracks") {
|
|
|
145
169
|
for (let index = 0; index < harmonyTracks.length; index += 1) {
|
|
146
170
|
const value = harmonyTracks[index];
|
|
147
171
|
if (value === void 0 || !Number.isFinite(value)) {
|
|
148
|
-
throw new (0,
|
|
172
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(`${name}[${index}] must be a track index; received ${value}`);
|
|
149
173
|
}
|
|
150
174
|
const track = Math.trunc(value);
|
|
151
175
|
if (track < 0 || track >= tracks.length) {
|
|
152
|
-
throw new (0,
|
|
176
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
153
177
|
`${name}[${index}] ${track} is outside the arrangement's ${tracks.length} tracks`
|
|
154
178
|
);
|
|
155
179
|
}
|
|
156
180
|
if (isPercussion(_optionalChain([tracks, 'access', _4 => _4[track], 'optionalAccess', _5 => _5.role]))) {
|
|
157
|
-
throw new (0,
|
|
181
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
158
182
|
`${name}[${index}] ${track} names a percussion track, whose pitches state no harmony`
|
|
159
183
|
);
|
|
160
184
|
}
|
|
@@ -195,7 +219,7 @@ function splitIntoSubVoices(ordered) {
|
|
|
195
219
|
continue;
|
|
196
220
|
}
|
|
197
221
|
const end = last.startBeat + last.durationBeat;
|
|
198
|
-
if (!
|
|
222
|
+
if (!_chunkLQZQDBCDcjs.hasEnded.call(void 0, end, note.startBeat)) {
|
|
199
223
|
continue;
|
|
200
224
|
}
|
|
201
225
|
const distance = Math.abs(last.pitch - note.pitch);
|
|
@@ -216,13 +240,13 @@ function splitIntoSubVoices(ordered) {
|
|
|
216
240
|
for (let start = 0; start < ordered.length; ) {
|
|
217
241
|
const onset = _nullishCoalesce(_optionalChain([ordered, 'access', _13 => _13[start], 'optionalAccess', _14 => _14.startBeat]), () => ( 0));
|
|
218
242
|
let end = start + 1;
|
|
219
|
-
while (end < ordered.length &&
|
|
243
|
+
while (end < ordered.length && _chunkLQZQDBCDcjs.sameInstant.call(void 0, _nullishCoalesce(_optionalChain([ordered, 'access', _15 => _15[end], 'optionalAccess', _16 => _16.startBeat]), () => ( 0)), onset)) {
|
|
220
244
|
end += 1;
|
|
221
245
|
}
|
|
222
246
|
const block = ordered.slice(start, end);
|
|
223
247
|
if (block.length > 1) {
|
|
224
248
|
const freeLanes = lanes.map((lane, index) => ({ index, last: lane[lane.length - 1] })).filter(
|
|
225
|
-
(entry) => entry.last !== void 0 &&
|
|
249
|
+
(entry) => entry.last !== void 0 && _chunkLQZQDBCDcjs.hasEnded.call(void 0, entry.last.startBeat + entry.last.durationBeat, onset)
|
|
226
250
|
).sort((a, b) => a.last.pitch - b.last.pitch);
|
|
227
251
|
const assignment = pairWithFreeLanes(block, freeLanes);
|
|
228
252
|
const taken = new Set(
|
|
@@ -310,7 +334,7 @@ function lastStartingAtOrBefore(sounding, beat) {
|
|
|
310
334
|
let high = sounding.length;
|
|
311
335
|
while (low < high) {
|
|
312
336
|
const middle = Math.floor((low + high) / 2);
|
|
313
|
-
if ((_nullishCoalesce(_optionalChain([sounding, 'access', _22 => _22[middle], 'optionalAccess', _23 => _23.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat +
|
|
337
|
+
if ((_nullishCoalesce(_optionalChain([sounding, 'access', _22 => _22[middle], 'optionalAccess', _23 => _23.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
314
338
|
low = middle + 1;
|
|
315
339
|
} else {
|
|
316
340
|
high = middle;
|
|
@@ -321,12 +345,17 @@ function lastStartingAtOrBefore(sounding, beat) {
|
|
|
321
345
|
function prepareTracks(tracks) {
|
|
322
346
|
let nextId = 0;
|
|
323
347
|
return tracks.map((track, index) => {
|
|
324
|
-
const ordered = track.notes.map((note, originalIndex) => ({ note, originalIndex })).filter(({ note }) => note.durationBeat > 0).sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch).map(({ note, originalIndex }) => ({
|
|
348
|
+
const ordered = track.notes.map((note, originalIndex) => ({ note, originalIndex })).filter(({ note }) => note.durationBeat > 0).sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch).map(({ note, originalIndex }) => ({
|
|
349
|
+
...note,
|
|
350
|
+
pitch: note.pitch === 0 ? 0 : note.pitch,
|
|
351
|
+
id: nextId++,
|
|
352
|
+
originalIndex
|
|
353
|
+
}));
|
|
325
354
|
const voices = splitIntoSubVoices(ordered).map((voice) => {
|
|
326
355
|
const sounding = voice.map((note, i) => {
|
|
327
356
|
const prev = i > 0 ? voice[i - 1] : void 0;
|
|
328
357
|
const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
|
|
329
|
-
const contiguous = prevEnd !== void 0 &&
|
|
358
|
+
const contiguous = prevEnd !== void 0 && _chunkLQZQDBCDcjs.adjacent.call(void 0, prevEnd, note.startBeat);
|
|
330
359
|
return {
|
|
331
360
|
id: note.id,
|
|
332
361
|
originalIndex: note.originalIndex,
|
|
@@ -359,24 +388,25 @@ function withGivenKeys(result, given) {
|
|
|
359
388
|
if (given === void 0) {
|
|
360
389
|
return result;
|
|
361
390
|
}
|
|
362
|
-
return { ...result, keys: given, prevailingKey: _nullishCoalesce(
|
|
391
|
+
return { ...result, keys: given, prevailingKey: _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, given), () => ( result.prevailingKey)) };
|
|
363
392
|
}
|
|
364
393
|
function callerTimeline(timeline, key, given, pooled, meters, budget) {
|
|
365
|
-
const chartEnd = timeline.segments.reduce(
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
0
|
|
394
|
+
const chartEnd = timeline.segments.reduce(
|
|
395
|
+
(end, segment) => Math.max(end, segment.endBeat),
|
|
396
|
+
Number.NEGATIVE_INFINITY
|
|
369
397
|
);
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
|
|
398
|
+
const soundingEnd = _chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, pooled, Number.NEGATIVE_INFINITY);
|
|
399
|
+
const spanEnd = Math.max(chartEnd, soundingEnd);
|
|
400
|
+
const totalBeats = Number.isFinite(spanEnd) ? spanEnd : 0;
|
|
401
|
+
const { startBeat: musicStart } = _chunkLQZQDBCDcjs.gridForNotes.call(void 0, pooled, _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, 0, meters));
|
|
402
|
+
const keys = _nullishCoalesce(given, () => ( _chunkLQZQDBCDcjs.attachPivots.call(void 0,
|
|
403
|
+
key !== void 0 ? _chunkLQZQDBCDcjs.statedKeyRegions.call(void 0, _chunkYDGMWH43cjs.resolveKey.call(void 0, key), musicStart, totalBeats) : _chunkLQZQDBCDcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }),
|
|
374
404
|
timeline.segments
|
|
375
405
|
)));
|
|
376
406
|
return {
|
|
377
407
|
timeline,
|
|
378
408
|
keys,
|
|
379
|
-
prevailingKey: _nullishCoalesce(
|
|
409
|
+
prevailingKey: _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunkYDGMWH43cjs.resolveKey.call(void 0, _chunkYDGMWH43cjs.majorKey.call(void 0, 0)))),
|
|
380
410
|
segmentConfidence: timeline.segments.map(() => 0)
|
|
381
411
|
};
|
|
382
412
|
}
|
|
@@ -393,7 +423,7 @@ function soundingVoicesAt(prepared, beat) {
|
|
|
393
423
|
continue;
|
|
394
424
|
}
|
|
395
425
|
const snap = { pitch: note.pitch };
|
|
396
|
-
const previous =
|
|
426
|
+
const previous = _chunkLQZQDBCDcjs.sameInstant.call(void 0, note.startBeat, beat) ? note.prevPitch : note.pitch;
|
|
397
427
|
if (previous !== void 0) {
|
|
398
428
|
snap.prevPitch = previous;
|
|
399
429
|
}
|
|
@@ -422,11 +452,11 @@ function snapshotsOf(voices) {
|
|
|
422
452
|
return voices.map(({ snapshot }) => snapshot);
|
|
423
453
|
}
|
|
424
454
|
function analyzePolyphony(notes, chordAtBeat, key) {
|
|
425
|
-
const passage =
|
|
426
|
-
|
|
455
|
+
const passage = _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "notes", { allowNonPositiveDuration: true });
|
|
456
|
+
_chunkS3QELJUEcjs.assertFunction.call(void 0, chordAtBeat, "chordAtBeat");
|
|
427
457
|
const prepared = prepareTracks([{ notes: [...passage] }]);
|
|
428
458
|
const track = prepared[0];
|
|
429
|
-
|
|
459
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
|
|
430
460
|
passage.length * (_nullishCoalesce(_optionalChain([track, 'optionalAccess', _29 => _29.voices, 'access', _30 => _30.length]), () => ( 0))),
|
|
431
461
|
"polyphony note-voice comparisons"
|
|
432
462
|
);
|
|
@@ -437,7 +467,7 @@ function analyzePolyphony(notes, chordAtBeat, key) {
|
|
|
437
467
|
if (subVoice === void 0) {
|
|
438
468
|
continue;
|
|
439
469
|
}
|
|
440
|
-
const analyzed =
|
|
470
|
+
const analyzed = _chunkLQZQDBCDcjs.analyzeVoice.call(void 0,
|
|
441
471
|
subVoice.voice,
|
|
442
472
|
chordAtBeat,
|
|
443
473
|
key,
|
|
@@ -455,7 +485,7 @@ function analyzePolyphony(notes, chordAtBeat, key) {
|
|
|
455
485
|
if (analyzed !== void 0) {
|
|
456
486
|
return analyzed;
|
|
457
487
|
}
|
|
458
|
-
const [only] =
|
|
488
|
+
const [only] = _chunkLQZQDBCDcjs.analyzeVoice.call(void 0, [{ ...note, id: index }], chordAtBeat, key);
|
|
459
489
|
return _nullishCoalesce(only, () => ( {
|
|
460
490
|
noteId: index,
|
|
461
491
|
pitch: note.pitch,
|
|
@@ -470,7 +500,7 @@ function firstSegmentAfter(timeline, beat) {
|
|
|
470
500
|
let high = timeline.segments.length;
|
|
471
501
|
while (low < high) {
|
|
472
502
|
const middle = Math.floor((low + high) / 2);
|
|
473
|
-
if ((_nullishCoalesce(_optionalChain([timeline, 'access', _35 => _35.segments, 'access', _36 => _36[middle], 'optionalAccess', _37 => _37.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat +
|
|
503
|
+
if ((_nullishCoalesce(_optionalChain([timeline, 'access', _35 => _35.segments, 'access', _36 => _36[middle], 'optionalAccess', _37 => _37.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
474
504
|
low = middle + 1;
|
|
475
505
|
} else {
|
|
476
506
|
high = middle;
|
|
@@ -483,7 +513,7 @@ function evaluationBeats(note, timeline) {
|
|
|
483
513
|
const beats = [note.startBeat];
|
|
484
514
|
for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {
|
|
485
515
|
const start = _optionalChain([timeline, 'access', _38 => _38.segments, 'access', _39 => _39[i], 'optionalAccess', _40 => _40.startBeat]);
|
|
486
|
-
if (start === void 0 || start >= noteEnd -
|
|
516
|
+
if (start === void 0 || start >= noteEnd - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
487
517
|
break;
|
|
488
518
|
}
|
|
489
519
|
beats.push(start);
|
|
@@ -494,19 +524,19 @@ function analyzeArrangement(tracks, opts = {}) {
|
|
|
494
524
|
return analyzeArrangementWith(tracks, opts).analysis;
|
|
495
525
|
}
|
|
496
526
|
function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
497
|
-
const meters =
|
|
527
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "arrangement meters");
|
|
498
528
|
const budget = opts.budget;
|
|
499
529
|
const noteOptions = { allowNonPositiveDuration: true, budget };
|
|
500
530
|
if (opts.pickupBeats !== void 0) {
|
|
501
|
-
|
|
531
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
|
|
502
532
|
noteOptions.minStartBeat = -opts.pickupBeats;
|
|
503
533
|
}
|
|
504
|
-
|
|
505
|
-
|
|
534
|
+
_chunkS3QELJUEcjs.assertArray.call(void 0, tracks, "arrangement tracks");
|
|
535
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
|
|
506
536
|
const noteCount = assertTrackNotes(tracks, noteOptions);
|
|
507
|
-
|
|
537
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
|
|
508
538
|
const profile = arrangementProfile(opts.profile);
|
|
509
|
-
const minSeverity = opts.minSeverity === void 0 ? 1 /* Warning */ :
|
|
539
|
+
const minSeverity = opts.minSeverity === void 0 ? 1 /* Warning */ : _chunkS3QELJUEcjs.assertInteger.call(void 0,
|
|
510
540
|
opts.minSeverity,
|
|
511
541
|
"arrangement minSeverity",
|
|
512
542
|
0 /* Safe */,
|
|
@@ -517,12 +547,12 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
517
547
|
const harmonyTracks = harmonyTrackSet(opts.harmonyTracks, tracks);
|
|
518
548
|
const prepared = prepareTracks(tracks);
|
|
519
549
|
const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
|
|
520
|
-
|
|
550
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, noteCount * voiceCount, "arrangement note-voice comparisons", budget);
|
|
521
551
|
const pooled = poolNotes(tracks, harmonyTracks);
|
|
522
552
|
let evidence;
|
|
523
553
|
let inferred;
|
|
524
554
|
if (opts.timeline === void 0) {
|
|
525
|
-
const run =
|
|
555
|
+
const run = _chunkLQZQDBCDcjs.analyzeTimeline.call(void 0,
|
|
526
556
|
pooled,
|
|
527
557
|
{
|
|
528
558
|
key: opts.key,
|
|
@@ -540,9 +570,9 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
540
570
|
inferred = callerTimeline(opts.timeline, opts.key, opts.keys, pooled, meters, budget);
|
|
541
571
|
}
|
|
542
572
|
const { timeline, keys, prevailingKey, segmentConfidence } = inferred;
|
|
543
|
-
const keyAt =
|
|
544
|
-
const scaleAt = (beat) =>
|
|
545
|
-
const cadences =
|
|
573
|
+
const keyAt = _chunkLQZQDBCDcjs.keyLookup.call(void 0, keys, prevailingKey);
|
|
574
|
+
const scaleAt = (beat) => _chunkYDGMWH43cjs.scaleOf.call(void 0, keyAt(beat));
|
|
575
|
+
const cadences = _chunkLQZQDBCDcjs.detectCadences.call(void 0, timeline, scaleAt);
|
|
546
576
|
const soundingCache = /* @__PURE__ */ new Map();
|
|
547
577
|
const trackAnalyses = [];
|
|
548
578
|
const conflicts = [];
|
|
@@ -561,7 +591,7 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
561
591
|
if (!subVoice) {
|
|
562
592
|
continue;
|
|
563
593
|
}
|
|
564
|
-
const analyzed =
|
|
594
|
+
const analyzed = _chunkLQZQDBCDcjs.analyzeVoice.call(void 0,
|
|
565
595
|
subVoice.voice,
|
|
566
596
|
timeline.at,
|
|
567
597
|
scaleAt,
|
|
@@ -578,7 +608,7 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
578
608
|
continue;
|
|
579
609
|
}
|
|
580
610
|
for (const beat of evaluationBeats(note, timeline)) {
|
|
581
|
-
const atOnset =
|
|
611
|
+
const atOnset = _chunkLQZQDBCDcjs.sameInstant.call(void 0, beat, note.startBeat);
|
|
582
612
|
const others = otherVoicesSounding(prepared, t, v, beat, soundingCache);
|
|
583
613
|
const safetyQuery = {
|
|
584
614
|
profile,
|
|
@@ -587,11 +617,11 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
587
617
|
chord: timeline.at(beat),
|
|
588
618
|
key: scaleAt(beat),
|
|
589
619
|
otherVoices: snapshotsOf(others),
|
|
590
|
-
strongBeat:
|
|
620
|
+
strongBeat: _chunkS3QELJUEcjs.isStrongBeat.call(void 0, beat, meters)
|
|
591
621
|
};
|
|
592
|
-
let result =
|
|
622
|
+
let result = _chunkYDGMWH43cjs.evaluateSafety.call(void 0, safetyQuery, { suggestions: false });
|
|
593
623
|
if (result.safety !== 0 /* Safe */ && result.safety >= minSeverity) {
|
|
594
|
-
result =
|
|
624
|
+
result = _chunkYDGMWH43cjs.evaluateSafety.call(void 0, safetyQuery, { partners: true });
|
|
595
625
|
const conflict = {
|
|
596
626
|
beat,
|
|
597
627
|
trackName: track.name,
|
|
@@ -704,7 +734,7 @@ function sessionOf(tracks, opts, analysis, evidence) {
|
|
|
704
734
|
tracks,
|
|
705
735
|
analysis,
|
|
706
736
|
update(edits) {
|
|
707
|
-
|
|
737
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, edits.length, "arrangement edits", opts.budget);
|
|
708
738
|
const next = [...tracks];
|
|
709
739
|
let edited = null;
|
|
710
740
|
let reordered = false;
|
|
@@ -715,16 +745,16 @@ function sessionOf(tracks, opts, analysis, evidence) {
|
|
|
715
745
|
}
|
|
716
746
|
const target = next[edit.trackIndex];
|
|
717
747
|
if (target === void 0) {
|
|
718
|
-
throw new (0,
|
|
748
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
719
749
|
`edits[${index}].trackIndex ${edit.trackIndex} is outside the session's ${tracks.length} tracks`
|
|
720
750
|
);
|
|
721
751
|
}
|
|
722
752
|
if (!Array.isArray(edit.notes)) {
|
|
723
|
-
throw new (0,
|
|
724
|
-
`edits[${index}].notes must be an array; received ${
|
|
753
|
+
throw new (0, _chunkS3QELJUEcjs.InvalidInputError)(
|
|
754
|
+
`edits[${index}].notes must be an array; received ${_chunkS3QELJUEcjs.describeRejected.call(void 0, edit.notes)}`
|
|
725
755
|
);
|
|
726
756
|
}
|
|
727
|
-
|
|
757
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, edit.notes, `edits[${index}].notes`, {
|
|
728
758
|
allowNonPositiveDuration: true,
|
|
729
759
|
budget: opts.budget
|
|
730
760
|
});
|
|
@@ -735,7 +765,7 @@ function sessionOf(tracks, opts, analysis, evidence) {
|
|
|
735
765
|
if (edited === null && !reordered) {
|
|
736
766
|
return sessionOf(next, opts, analysis, evidence);
|
|
737
767
|
}
|
|
738
|
-
const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } :
|
|
768
|
+
const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } : _chunkLQZQDBCDcjs.dirtySlotsFor.call(void 0, evidence, edited.startBeat, edited.endBeat);
|
|
739
769
|
const run = analyzeArrangementWith(next, opts, evidence, dirty);
|
|
740
770
|
return sessionOf(next, opts, run.analysis, run.evidence);
|
|
741
771
|
}
|
|
@@ -756,26 +786,27 @@ var FUNCTION_WEIGHT = 0.5;
|
|
|
756
786
|
var DISSONANCE_WEIGHT = 0.35;
|
|
757
787
|
var SPAN_WEIGHT = 0.15;
|
|
758
788
|
var SPAN_SATURATION = 24;
|
|
789
|
+
var MAX_DISTINCT_PITCHES = 128;
|
|
759
790
|
function clamp012(value) {
|
|
760
791
|
return Math.min(1, Math.max(0, value));
|
|
761
792
|
}
|
|
762
793
|
function tensionCurve(tracks, opts = {}) {
|
|
763
|
-
const meters =
|
|
794
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "arrangement meters");
|
|
764
795
|
const budget = opts.budget;
|
|
765
796
|
const noteOptions = { allowNonPositiveDuration: true, budget };
|
|
766
797
|
if (opts.pickupBeats !== void 0) {
|
|
767
|
-
|
|
798
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, "arrangement pickupBeats");
|
|
768
799
|
noteOptions.minStartBeat = -opts.pickupBeats;
|
|
769
800
|
}
|
|
770
|
-
|
|
771
|
-
|
|
801
|
+
_chunkS3QELJUEcjs.assertArray.call(void 0, tracks, "arrangement tracks");
|
|
802
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
|
|
772
803
|
const noteCount = assertTrackNotes(tracks, noteOptions);
|
|
773
|
-
|
|
804
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
|
|
774
805
|
const profile = arrangementProfile(opts.profile);
|
|
775
806
|
const step = _nullishCoalesce(opts.step, () => ( 1));
|
|
776
|
-
|
|
807
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
|
|
777
808
|
if (opts.minSeverity !== void 0) {
|
|
778
|
-
|
|
809
|
+
_chunkS3QELJUEcjs.assertInteger.call(void 0,
|
|
779
810
|
opts.minSeverity,
|
|
780
811
|
"arrangement minSeverity",
|
|
781
812
|
0 /* Safe */,
|
|
@@ -787,20 +818,15 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
787
818
|
const harmonyTracks = harmonyTrackSet(opts.harmonyTracks, tracks);
|
|
788
819
|
const pooled = poolNotes(tracks, harmonyTracks);
|
|
789
820
|
const all = poolNotes(tracks);
|
|
790
|
-
const totalBeats =
|
|
791
|
-
const { origin: sampleStart, startBeat: musicStart } =
|
|
792
|
-
const suppliedKeys = opts.timeline === void 0 ? void 0 : _nullishCoalesce(opts.keys, () => ( (opts.key !== void 0 ?
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, opts.key),
|
|
800
|
-
confidence: 1
|
|
801
|
-
}
|
|
802
|
-
] : _chunkSGLEQVJHcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }))));
|
|
803
|
-
const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ? _chunkSGLEQVJHcjs.chordTimelineFromNotes.call(void 0, pooled, {
|
|
821
|
+
const totalBeats = _chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, all);
|
|
822
|
+
const { origin: sampleStart, startBeat: musicStart } = _chunkLQZQDBCDcjs.gridForNotes.call(void 0, all, step);
|
|
823
|
+
const suppliedKeys = opts.timeline === void 0 ? void 0 : _nullishCoalesce(opts.keys, () => ( (opts.key !== void 0 ? (
|
|
824
|
+
// The one region a named key holds starts where the music does, as
|
|
825
|
+
// the searched regions and the timeline's own key window do: a key
|
|
826
|
+
// cannot be in force over beats nothing sounds in.
|
|
827
|
+
_chunkLQZQDBCDcjs.statedKeyRegions.call(void 0, _chunkYDGMWH43cjs.resolveKey.call(void 0, opts.key), musicStart, totalBeats)
|
|
828
|
+
) : _chunkLQZQDBCDcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }))));
|
|
829
|
+
const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ? _chunkLQZQDBCDcjs.chordTimelineFromNotes.call(void 0, pooled, {
|
|
804
830
|
key: opts.key,
|
|
805
831
|
meters,
|
|
806
832
|
pickupBeats: opts.pickupBeats,
|
|
@@ -809,17 +835,17 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
809
835
|
}) : {
|
|
810
836
|
timeline: opts.timeline,
|
|
811
837
|
keys: suppliedKeys,
|
|
812
|
-
prevailingKey: _nullishCoalesce(
|
|
838
|
+
prevailingKey: _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, suppliedKeys), () => ( _chunkYDGMWH43cjs.resolveKey.call(void 0, _chunkYDGMWH43cjs.majorKey.call(void 0, 0))))
|
|
813
839
|
};
|
|
814
840
|
const regions = _nullishCoalesce(opts.keys, () => ( keys));
|
|
815
|
-
const keyAt =
|
|
841
|
+
const keyAt = _chunkLQZQDBCDcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, regions), () => ( prevailingKey)));
|
|
816
842
|
const prepared = prepareTracks(tracks);
|
|
817
843
|
const points = [];
|
|
818
|
-
const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step -
|
|
819
|
-
|
|
844
|
+
const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step - _chunkS3QELJUEcjs.BEAT_EPS));
|
|
845
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, sampleCount, "tension samples", budget);
|
|
820
846
|
const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
|
|
821
|
-
|
|
822
|
-
sampleCount * Math.min(
|
|
847
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
|
|
848
|
+
sampleCount * Math.min(MAX_DISTINCT_PITCHES, voiceCount) * voiceCount,
|
|
823
849
|
"tension sample-voice comparisons",
|
|
824
850
|
budget
|
|
825
851
|
);
|
|
@@ -827,16 +853,15 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
827
853
|
const beat = i === 0 ? Math.max(sampleStart, musicStart) : sampleStart + i * step;
|
|
828
854
|
points.push({
|
|
829
855
|
beat,
|
|
830
|
-
// The
|
|
831
|
-
|
|
832
|
-
tension: sampleTension(prepared, timeline, _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat)), meters, profile, beat)
|
|
856
|
+
// The key whole: an augmented sixth is told from a dominant by its letters.
|
|
857
|
+
tension: sampleTension(prepared, timeline, keyAt(beat), meters, profile, beat)
|
|
833
858
|
});
|
|
834
859
|
}
|
|
835
860
|
return points;
|
|
836
861
|
}
|
|
837
862
|
function tensionCurveFrom(tracks, analysis, opts = {}) {
|
|
838
|
-
const asked =
|
|
839
|
-
const carried = carriedHarmony(
|
|
863
|
+
const asked = _chunkS3QELJUEcjs.assertRecord.call(void 0, opts, "opts");
|
|
864
|
+
const carried = carriedHarmony(_chunkS3QELJUEcjs.assertRecord.call(void 0, analysis, "analysis"), asked);
|
|
840
865
|
return tensionCurve(tracks, { ...carried, ...asked });
|
|
841
866
|
}
|
|
842
867
|
function carriedHarmony(analysis, opts) {
|
|
@@ -870,9 +895,9 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
|
|
|
870
895
|
return 0;
|
|
871
896
|
}
|
|
872
897
|
const chord = timeline.at(beat);
|
|
873
|
-
const functionScore = chord ? FUNCTION_TENSION[
|
|
874
|
-
const chordPcs = chord ? new Set(
|
|
875
|
-
const strongBeat =
|
|
898
|
+
const functionScore = chord ? FUNCTION_TENSION[_chunkLQZQDBCDcjs.functionOf.call(void 0, chord, key)] : 0;
|
|
899
|
+
const chordPcs = chord ? new Set(_chunkYDGMWH43cjs.chordPitchClasses.call(void 0, chord)) : null;
|
|
900
|
+
const strongBeat = _chunkS3QELJUEcjs.isStrongBeat.call(void 0, beat, meters);
|
|
876
901
|
let nonChord = 0;
|
|
877
902
|
let dissonant = 0;
|
|
878
903
|
const pitches = /* @__PURE__ */ new Map();
|
|
@@ -888,7 +913,7 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
|
|
|
888
913
|
}
|
|
889
914
|
}
|
|
890
915
|
for (const [pitch, count] of pitches) {
|
|
891
|
-
if (chordPcs && !chordPcs.has(
|
|
916
|
+
if (chordPcs && !chordPcs.has(_chunkS3QELJUEcjs.pitchClassOf.call(void 0, pitch))) {
|
|
892
917
|
nonChord += count;
|
|
893
918
|
}
|
|
894
919
|
const at = _nullishCoalesce(firstOf.get(pitch), () => ( 0));
|
|
@@ -899,8 +924,8 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
|
|
|
899
924
|
others[at] = tail;
|
|
900
925
|
others.pop();
|
|
901
926
|
}
|
|
902
|
-
const result =
|
|
903
|
-
{ profile, candidatePitch: pitch, chord, key, otherVoices: others, strongBeat },
|
|
927
|
+
const result = _chunkYDGMWH43cjs.evaluateSafety.call(void 0,
|
|
928
|
+
{ profile, candidatePitch: pitch, chord, key: key.scale, otherVoices: others, strongBeat },
|
|
904
929
|
{ suggestions: false }
|
|
905
930
|
);
|
|
906
931
|
if (held !== void 0 && tail !== void 0) {
|
|
@@ -926,19 +951,34 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
|
|
|
926
951
|
|
|
927
952
|
// src/analyze/form/hypermeter.ts
|
|
928
953
|
var GROUP_CANDIDATES = [2, 3, 4, 6, 8];
|
|
929
|
-
var
|
|
954
|
+
var GROUP_PRIOR_LIST = [
|
|
930
955
|
[2, 0.9],
|
|
931
956
|
[3, 0.75],
|
|
932
957
|
[4, 1],
|
|
933
958
|
[6, 0.7],
|
|
934
959
|
[8, 0.85]
|
|
935
|
-
]
|
|
960
|
+
];
|
|
961
|
+
var GROUP_PRIOR = new Map(GROUP_PRIOR_LIST);
|
|
936
962
|
var CONTRAST_SHARE = 0.65;
|
|
937
963
|
var CADENCE_SHARE = 0.35;
|
|
938
964
|
var GROUP_FINAL_FIT = 1;
|
|
939
965
|
var ELIDED_FIT = 0.5;
|
|
940
966
|
var MIN_GROUPS = 2;
|
|
941
967
|
var MARGIN_FLOOR = 0.7;
|
|
968
|
+
function cadenceSpans(arrivals, segments) {
|
|
969
|
+
return arrivals.map(({ atBeat, strength }) => ({
|
|
970
|
+
atBeat,
|
|
971
|
+
endBeat: _nullishCoalesce(_optionalChain([_chunkLQZQDBCDcjs.segmentStartingAt.call(void 0, segments, atBeat), 'optionalAccess', _41 => _41.endBeat]), () => ( atBeat)),
|
|
972
|
+
strength
|
|
973
|
+
}));
|
|
974
|
+
}
|
|
975
|
+
function cadenceBarsOf(spans, meter) {
|
|
976
|
+
return spans.map((span) => ({
|
|
977
|
+
arrival: _chunkS3QELJUEcjs.barIndexAt.call(void 0, span.atBeat, meter),
|
|
978
|
+
last: _chunkLQZQDBCDcjs.lastBarOf.call(void 0, meter, span.atBeat, span.endBeat),
|
|
979
|
+
strength: span.strength
|
|
980
|
+
}));
|
|
981
|
+
}
|
|
942
982
|
function accentContrast(novelty, slices, groupBars, phase) {
|
|
943
983
|
let headSum = 0;
|
|
944
984
|
let headCount = 0;
|
|
@@ -950,7 +990,7 @@ function accentContrast(novelty, slices, groupBars, phase) {
|
|
|
950
990
|
continue;
|
|
951
991
|
}
|
|
952
992
|
const value = _nullishCoalesce(novelty[i], () => ( 0));
|
|
953
|
-
if (
|
|
993
|
+
if (_chunkLQZQDBCDcjs.floorMod.call(void 0, slice.index - phase, groupBars) === 0) {
|
|
954
994
|
headSum += value;
|
|
955
995
|
headCount += 1;
|
|
956
996
|
} else {
|
|
@@ -963,22 +1003,41 @@ function accentContrast(novelty, slices, groupBars, phase) {
|
|
|
963
1003
|
}
|
|
964
1004
|
return headSum / headCount - otherSum / otherCount;
|
|
965
1005
|
}
|
|
966
|
-
function cadenceFitOf(
|
|
967
|
-
const lastBar = _nullishCoalesce(_optionalChain([slices, 'access',
|
|
1006
|
+
function cadenceFitOf(cadences, slices, groupBars, phase) {
|
|
1007
|
+
const lastBar = _nullishCoalesce(_optionalChain([slices, 'access', _42 => _42[slices.length - 1], 'optionalAccess', _43 => _43.index]), () => ( -1));
|
|
1008
|
+
const closing = /* @__PURE__ */ new Map();
|
|
1009
|
+
const eliding = /* @__PURE__ */ new Map();
|
|
1010
|
+
const credit = (bars, bar, strength) => {
|
|
1011
|
+
bars.set(bar, Math.max(_nullishCoalesce(bars.get(bar), () => ( 0)), strength));
|
|
1012
|
+
};
|
|
1013
|
+
for (const cadence of cadences) {
|
|
1014
|
+
const end = cadence.arrival + _chunkLQZQDBCDcjs.floorMod.call(void 0, groupBars - 1 + phase - cadence.arrival, groupBars);
|
|
1015
|
+
if (end <= cadence.last) {
|
|
1016
|
+
credit(closing, end, cadence.strength);
|
|
1017
|
+
} else if (_chunkLQZQDBCDcjs.floorMod.call(void 0, cadence.arrival - phase, groupBars) === 0 && // Elision needs a next group to arrive into. Past the last bar the span
|
|
1018
|
+
// holds there is none, so a cadence beat beyond the music is not evidence
|
|
1019
|
+
// about how the music that was read is grouped.
|
|
1020
|
+
cadence.arrival <= lastBar) {
|
|
1021
|
+
credit(eliding, cadence.arrival - 1, cadence.strength);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
968
1024
|
let ends = 0;
|
|
969
|
-
let
|
|
1025
|
+
let closed = 0;
|
|
1026
|
+
let weighted = 0;
|
|
970
1027
|
for (const slice of slices) {
|
|
971
|
-
if (slice.index < 0 ||
|
|
1028
|
+
if (slice.index < 0 || _chunkLQZQDBCDcjs.floorMod.call(void 0, slice.index - phase, groupBars) !== groupBars - 1) {
|
|
972
1029
|
continue;
|
|
973
1030
|
}
|
|
974
1031
|
ends += 1;
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1032
|
+
const final = closing.get(slice.index);
|
|
1033
|
+
const elided = eliding.get(slice.index);
|
|
1034
|
+
closed += Math.max(
|
|
1035
|
+
final === void 0 ? 0 : GROUP_FINAL_FIT,
|
|
1036
|
+
elided === void 0 ? 0 : ELIDED_FIT
|
|
1037
|
+
);
|
|
1038
|
+
weighted += Math.max(GROUP_FINAL_FIT * (_nullishCoalesce(final, () => ( 0))), ELIDED_FIT * (_nullishCoalesce(elided, () => ( 0))));
|
|
980
1039
|
}
|
|
981
|
-
return ends === 0 ? 0 :
|
|
1040
|
+
return ends === 0 ? { closed: 0, weighted: 0 } : { closed: _chunkLQZQDBCDcjs.clamp01.call(void 0, closed / ends), weighted: _chunkLQZQDBCDcjs.clamp01.call(void 0, weighted / ends) };
|
|
982
1041
|
}
|
|
983
1042
|
function undecidedReading(slices) {
|
|
984
1043
|
const fullBars = slices.filter((slice) => slice.index >= 0);
|
|
@@ -992,52 +1051,70 @@ function undecidedReading(slices) {
|
|
|
992
1051
|
};
|
|
993
1052
|
}
|
|
994
1053
|
function hypermeter(notes, meter, opts = {}) {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1054
|
+
return hypermeterOver(
|
|
1055
|
+
notes,
|
|
1056
|
+
meter,
|
|
1057
|
+
opts,
|
|
1058
|
+
(asked) => cadenceSpans(
|
|
1059
|
+
(_nullishCoalesce(asked.cadenceBeats, () => ( []))).map((atBeat) => ({ atBeat, strength: 1 })),
|
|
1060
|
+
[]
|
|
1061
|
+
)
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
function hypermeterOver(notes, meter, opts, spansOf) {
|
|
1065
|
+
const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "opts");
|
|
1066
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, { meters: meter }, "meter");
|
|
1067
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "hypermeter notes", {
|
|
998
1068
|
allowNonPositiveDuration: true,
|
|
999
1069
|
budget: asked.budget
|
|
1000
1070
|
});
|
|
1001
1071
|
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
1002
|
-
const firstOnset =
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1072
|
+
const firstOnset = _chunkLQZQDBCDcjs.firstSoundingBeat.call(void 0, sounding);
|
|
1073
|
+
const spanEnd = _chunkLQZQDBCDcjs.resolveSpanEnd.call(void 0,
|
|
1074
|
+
asked.totalBeats,
|
|
1075
|
+
firstOnset,
|
|
1076
|
+
_chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, sounding),
|
|
1077
|
+
"hypermeter totalBeats"
|
|
1078
|
+
);
|
|
1079
|
+
const spanStart = _chunkLQZQDBCDcjs.barGridStart.call(void 0, firstOnset, meters);
|
|
1080
|
+
if (sounding.length === 0 || spanEnd <= spanStart + _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1081
|
+
return undecidedReading([]);
|
|
1082
|
+
}
|
|
1083
|
+
const slices = _chunkLQZQDBCDcjs.sliceBars.call(void 0, sounding, meters, spanStart, spanEnd, asked.budget);
|
|
1008
1084
|
const fullBars = slices.reduce((count, slice) => slice.index >= 0 ? count + 1 : count, 0);
|
|
1009
1085
|
const candidates = GROUP_CANDIDATES.filter((groupBars) => fullBars >= groupBars * MIN_GROUPS);
|
|
1010
1086
|
if (candidates.length === 0) {
|
|
1011
1087
|
return undecidedReading(slices);
|
|
1012
1088
|
}
|
|
1013
|
-
|
|
1089
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0,
|
|
1014
1090
|
candidates.reduce((sum, groupBars) => sum + groupBars * slices.length, 0),
|
|
1015
1091
|
"hypermeter readings",
|
|
1016
1092
|
asked.budget
|
|
1017
1093
|
);
|
|
1018
|
-
const novelty = slices.map((_, index) =>
|
|
1019
|
-
const cadenceBars =
|
|
1020
|
-
const hasCadences = cadenceBars.
|
|
1094
|
+
const novelty = slices.map((_, index) => _chunkLQZQDBCDcjs.harmonicNovelty.call(void 0, slices, index));
|
|
1095
|
+
const cadenceBars = cadenceBarsOf(spansOf(asked), meters);
|
|
1096
|
+
const hasCadences = cadenceBars.length > 0;
|
|
1021
1097
|
const readings = [];
|
|
1022
1098
|
for (const groupBars of candidates) {
|
|
1023
1099
|
const phases = Array.from({ length: groupBars }, (_, phase) => ({
|
|
1024
1100
|
phase,
|
|
1025
1101
|
contrast: accentContrast(novelty, slices, groupBars, phase),
|
|
1026
|
-
|
|
1102
|
+
fit: cadenceFitOf(cadenceBars, slices, groupBars, phase)
|
|
1027
1103
|
}));
|
|
1028
|
-
const bestFit = phases.reduce((best2, reading) => Math.max(best2, reading.
|
|
1029
|
-
for (const { phase, contrast,
|
|
1030
|
-
if (hasCadences &&
|
|
1104
|
+
const bestFit = phases.reduce((best2, reading) => Math.max(best2, reading.fit.weighted), 0);
|
|
1105
|
+
for (const { phase, contrast, fit } of phases) {
|
|
1106
|
+
if (hasCadences && fit.weighted < bestFit - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1031
1107
|
continue;
|
|
1032
1108
|
}
|
|
1033
|
-
const
|
|
1109
|
+
const cadenceFit = fit.closed;
|
|
1110
|
+
const evidence = _chunkLQZQDBCDcjs.clamp01.call(void 0,
|
|
1034
1111
|
hasCadences ? (
|
|
1035
1112
|
// Floored at zero rather than summed raw: group heads that restate
|
|
1036
1113
|
// the harmony their group opened with — the commonest way a phrase
|
|
1037
1114
|
// begins — change less than the bars between them, and a negative
|
|
1038
1115
|
// contrast subtracted from the cadence term would count that
|
|
1039
1116
|
// restatement as evidence against the very phase it confirms.
|
|
1040
|
-
CONTRAST_SHARE *
|
|
1117
|
+
CONTRAST_SHARE * _chunkLQZQDBCDcjs.clamp01.call(void 0, contrast) + CADENCE_SHARE * cadenceFit
|
|
1041
1118
|
) : contrast
|
|
1042
1119
|
);
|
|
1043
1120
|
readings.push({
|
|
@@ -1058,8 +1135,8 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1058
1135
|
return undecidedReading(slices);
|
|
1059
1136
|
}
|
|
1060
1137
|
const runnerUp = readings.find((reading) => reading.groupBars !== best.groupBars);
|
|
1061
|
-
const margin = runnerUp === void 0 || best.score <=
|
|
1062
|
-
const downbeats = slices.filter((slice) => slice.index >= 0 &&
|
|
1138
|
+
const margin = runnerUp === void 0 || best.score <= _chunkS3QELJUEcjs.BEAT_EPS ? 1 : _chunkLQZQDBCDcjs.clamp01.call(void 0, (best.score - runnerUp.score) / best.score);
|
|
1139
|
+
const downbeats = slices.filter((slice) => slice.index >= 0 && _chunkLQZQDBCDcjs.floorMod.call(void 0, slice.index - best.phase, best.groupBars) === 0).map((slice) => slice.startBeat);
|
|
1063
1140
|
const cadencePart = hasCadences ? `${Math.round(best.cadenceFit * 100)}% of group ends closed by a cadence` : "no cadences given";
|
|
1064
1141
|
return {
|
|
1065
1142
|
groupBars: best.groupBars,
|
|
@@ -1069,554 +1146,11 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1069
1146
|
// runner-up is nearly always a multiple or a divisor of the winner — four
|
|
1070
1147
|
// bars nest inside eight — and a reading of the same accents at another
|
|
1071
1148
|
// level is weaker counter-evidence than an unrelated rival would be.
|
|
1072
|
-
confidence:
|
|
1149
|
+
confidence: _chunkLQZQDBCDcjs.clamp01.call(void 0, best.evidence * (MARGIN_FLOOR + (1 - MARGIN_FLOOR) * margin)),
|
|
1073
1150
|
rationale: `${best.groupBars}-bar groups from bar ${best.phase}: harmonic change ${Math.round(best.contrast * 100)}% stronger on group heads, ${cadencePart}`
|
|
1074
1151
|
};
|
|
1075
1152
|
}
|
|
1076
1153
|
|
|
1077
|
-
// src/analyze/melody/internal.ts
|
|
1078
|
-
var RHYTHM_GRID = 64;
|
|
1079
|
-
function phraseNotes(phrase) {
|
|
1080
|
-
if (Array.isArray(phrase)) {
|
|
1081
|
-
return phrase;
|
|
1082
|
-
}
|
|
1083
|
-
return _chunkS5BKZZGMcjs.assertRecord.call(void 0, phrase, "melodic phrase").notes;
|
|
1084
|
-
}
|
|
1085
|
-
function orderedNotes(notes, name, budget) {
|
|
1086
|
-
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, name, { allowNonPositiveDuration: true, budget });
|
|
1087
|
-
const sounding = notes.filter((note) => note.durationBeat > 0).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
|
|
1088
|
-
const line = [];
|
|
1089
|
-
for (const note of sounding) {
|
|
1090
|
-
const previous = line[line.length - 1];
|
|
1091
|
-
if (previous !== void 0 && Math.abs(note.startBeat - previous.startBeat) <= _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1092
|
-
line[line.length - 1] = note;
|
|
1093
|
-
continue;
|
|
1094
|
-
}
|
|
1095
|
-
line.push(note);
|
|
1096
|
-
}
|
|
1097
|
-
return line;
|
|
1098
|
-
}
|
|
1099
|
-
function intervalsOf(notes) {
|
|
1100
|
-
const out = [];
|
|
1101
|
-
for (let i = 1; i < notes.length; i += 1) {
|
|
1102
|
-
out.push((_nullishCoalesce(_optionalChain([notes, 'access', _43 => _43[i], 'optionalAccess', _44 => _44.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _45 => _45[i - 1], 'optionalAccess', _46 => _46.pitch]), () => ( 0))));
|
|
1103
|
-
}
|
|
1104
|
-
return out;
|
|
1105
|
-
}
|
|
1106
|
-
function onsetGaps(notes) {
|
|
1107
|
-
const out = [];
|
|
1108
|
-
for (let i = 1; i < notes.length; i += 1) {
|
|
1109
|
-
out.push((_nullishCoalesce(_optionalChain([notes, 'access', _47 => _47[i], 'optionalAccess', _48 => _48.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _49 => _49[i - 1], 'optionalAccess', _50 => _50.startBeat]), () => ( 0))));
|
|
1110
|
-
}
|
|
1111
|
-
return out;
|
|
1112
|
-
}
|
|
1113
|
-
function retrogradeGaps(notes) {
|
|
1114
|
-
const out = [];
|
|
1115
|
-
for (let i = notes.length - 1; i >= 1; i -= 1) {
|
|
1116
|
-
const later = notes[i];
|
|
1117
|
-
const earlier = notes[i - 1];
|
|
1118
|
-
out.push(
|
|
1119
|
-
(_nullishCoalesce(_optionalChain([later, 'optionalAccess', _51 => _51.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([later, 'optionalAccess', _52 => _52.durationBeat]), () => ( 0))) - ((_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _53 => _53.startBeat]), () => ( 0))) + (_nullishCoalesce(_optionalChain([earlier, 'optionalAccess', _54 => _54.durationBeat]), () => ( 0))))
|
|
1120
|
-
);
|
|
1121
|
-
}
|
|
1122
|
-
return out;
|
|
1123
|
-
}
|
|
1124
|
-
function rhythmProfile(gaps) {
|
|
1125
|
-
const unit = _nullishCoalesce(gaps[0], () => ( 0));
|
|
1126
|
-
if (!(unit > _chunkS5BKZZGMcjs.BEAT_EPS)) {
|
|
1127
|
-
return gaps.length === 0 ? [] : null;
|
|
1128
|
-
}
|
|
1129
|
-
return gaps.map((gap) => Math.round(gap / unit * RHYTHM_GRID) / RHYTHM_GRID);
|
|
1130
|
-
}
|
|
1131
|
-
function sameNumbers(a, b) {
|
|
1132
|
-
if (a.length !== b.length) {
|
|
1133
|
-
return false;
|
|
1134
|
-
}
|
|
1135
|
-
for (let i = 0; i < a.length; i += 1) {
|
|
1136
|
-
if (Math.abs((_nullishCoalesce(a[i], () => ( 0))) - (_nullishCoalesce(b[i], () => ( 0)))) > _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
return true;
|
|
1141
|
-
}
|
|
1142
|
-
function roundTo(value, digits) {
|
|
1143
|
-
const scale = 10 ** digits;
|
|
1144
|
-
return Math.round(value * scale) / scale;
|
|
1145
|
-
}
|
|
1146
|
-
function endBeatOf(notes) {
|
|
1147
|
-
let end = 0;
|
|
1148
|
-
for (const note of notes) {
|
|
1149
|
-
end = Math.max(end, note.startBeat + note.durationBeat);
|
|
1150
|
-
}
|
|
1151
|
-
return end;
|
|
1152
|
-
}
|
|
1153
|
-
function onsetSpan(notes) {
|
|
1154
|
-
if (notes.length < 2) {
|
|
1155
|
-
return 0;
|
|
1156
|
-
}
|
|
1157
|
-
return (_nullishCoalesce(_optionalChain([notes, 'access', _55 => _55[notes.length - 1], 'optionalAccess', _56 => _56.startBeat]), () => ( 0))) - (_nullishCoalesce(_optionalChain([notes, 'access', _57 => _57[0], 'optionalAccess', _58 => _58.startBeat]), () => ( 0)));
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
// src/analyze/melody/contour.ts
|
|
1161
|
-
var DIRECTIONAL_SHARE = 0.75;
|
|
1162
|
-
function extremes(notes) {
|
|
1163
|
-
if (notes.length === 0) {
|
|
1164
|
-
return { peak: -1, trough: -1 };
|
|
1165
|
-
}
|
|
1166
|
-
let peak = 0;
|
|
1167
|
-
let trough = 0;
|
|
1168
|
-
for (let i = 1; i < notes.length; i += 1) {
|
|
1169
|
-
if ((_nullishCoalesce(_optionalChain([notes, 'access', _59 => _59[i], 'optionalAccess', _60 => _60.pitch]), () => ( 0))) > (_nullishCoalesce(_optionalChain([notes, 'access', _61 => _61[peak], 'optionalAccess', _62 => _62.pitch]), () => ( 0)))) {
|
|
1170
|
-
peak = i;
|
|
1171
|
-
}
|
|
1172
|
-
if ((_nullishCoalesce(_optionalChain([notes, 'access', _63 => _63[i], 'optionalAccess', _64 => _64.pitch]), () => ( 0))) < (_nullishCoalesce(_optionalChain([notes, 'access', _65 => _65[trough], 'optionalAccess', _66 => _66.pitch]), () => ( 0)))) {
|
|
1173
|
-
trough = i;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
return { peak, trough };
|
|
1177
|
-
}
|
|
1178
|
-
function melodicContour(notes) {
|
|
1179
|
-
const line = orderedNotes(phraseNotes(notes), "contour notes");
|
|
1180
|
-
const intervals = intervalsOf(line);
|
|
1181
|
-
const directions = intervals.map(
|
|
1182
|
-
(step) => step > 0 ? "up" : step < 0 ? "down" : "same"
|
|
1183
|
-
);
|
|
1184
|
-
const { peak, trough } = extremes(line);
|
|
1185
|
-
const range = (_nullishCoalesce(_optionalChain([line, 'access', _67 => _67[peak], 'optionalAccess', _68 => _68.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([line, 'access', _69 => _69[trough], 'optionalAccess', _70 => _70.pitch]), () => ( 0)));
|
|
1186
|
-
const motion = intervals.reduce((sum, step) => sum + Math.abs(step), 0);
|
|
1187
|
-
if (motion === 0) {
|
|
1188
|
-
return {
|
|
1189
|
-
directions,
|
|
1190
|
-
shape: "static",
|
|
1191
|
-
peakIndex: peak,
|
|
1192
|
-
troughIndex: trough,
|
|
1193
|
-
range: 0,
|
|
1194
|
-
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"
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
const first = _nullishCoalesce(_optionalChain([line, 'access', _71 => _71[0], 'optionalAccess', _72 => _72.pitch]), () => ( 0));
|
|
1198
|
-
const last = _nullishCoalesce(_optionalChain([line, 'access', _73 => _73[line.length - 1], 'optionalAccess', _74 => _74.pitch]), () => ( 0));
|
|
1199
|
-
const net = last - first;
|
|
1200
|
-
const directness = Math.abs(net) / motion;
|
|
1201
|
-
const share = Math.round(directness * 100);
|
|
1202
|
-
if (directness >= DIRECTIONAL_SHARE) {
|
|
1203
|
-
const shape = net > 0 ? "ascending" : "descending";
|
|
1204
|
-
return {
|
|
1205
|
-
directions,
|
|
1206
|
-
shape,
|
|
1207
|
-
peakIndex: peak,
|
|
1208
|
-
troughIndex: trough,
|
|
1209
|
-
range,
|
|
1210
|
-
rationale: `${share}% of the motion runs ${net > 0 ? "up" : "down"}, so the line is ${shape}`
|
|
1211
|
-
};
|
|
1212
|
-
}
|
|
1213
|
-
const peakPitch = _nullishCoalesce(_optionalChain([line, 'access', _75 => _75[peak], 'optionalAccess', _76 => _76.pitch]), () => ( 0));
|
|
1214
|
-
const archMotion = peakPitch - first + (peakPitch - last);
|
|
1215
|
-
if (peak > 0 && peak < line.length - 1 && archMotion >= DIRECTIONAL_SHARE * motion) {
|
|
1216
|
-
return {
|
|
1217
|
-
directions,
|
|
1218
|
-
shape: "arch",
|
|
1219
|
-
peakIndex: peak,
|
|
1220
|
-
troughIndex: trough,
|
|
1221
|
-
range,
|
|
1222
|
-
rationale: `Rises to its peak at note ${peak + 1} of ${line.length} and falls back, so the line is an arch`
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
return {
|
|
1226
|
-
directions,
|
|
1227
|
-
shape: "wave",
|
|
1228
|
-
peakIndex: peak,
|
|
1229
|
-
troughIndex: trough,
|
|
1230
|
-
range,
|
|
1231
|
-
rationale: `Changes direction without settling either way (${share}% of the motion nets out), so the line is a wave`
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
// src/analyze/melody/motifs.ts
|
|
1236
|
-
var MIN_CELL_NOTES = 3;
|
|
1237
|
-
var MAX_CELL_NOTES = 8;
|
|
1238
|
-
var MIN_OCCURRENCES = 2;
|
|
1239
|
-
function windowSignature(notes) {
|
|
1240
|
-
const profile = rhythmProfile(onsetGaps(notes));
|
|
1241
|
-
if (profile === null) {
|
|
1242
|
-
return null;
|
|
1243
|
-
}
|
|
1244
|
-
return `${notes.length}|${intervalsOf(notes).join(",")}|${profile.join(",")}`;
|
|
1245
|
-
}
|
|
1246
|
-
function nonOverlapping(starts, length) {
|
|
1247
|
-
const picked = [];
|
|
1248
|
-
let lastEnd = Number.NEGATIVE_INFINITY;
|
|
1249
|
-
for (const start of starts) {
|
|
1250
|
-
if (start > lastEnd) {
|
|
1251
|
-
picked.push(start);
|
|
1252
|
-
lastEnd = start + length - 1;
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
return picked;
|
|
1256
|
-
}
|
|
1257
|
-
function subsumes(outer, inner) {
|
|
1258
|
-
let at = 0;
|
|
1259
|
-
for (const start of inner.starts) {
|
|
1260
|
-
while (at + 1 < outer.starts.length && (_nullishCoalesce(outer.starts[at + 1], () => ( 0))) <= start) {
|
|
1261
|
-
at += 1;
|
|
1262
|
-
}
|
|
1263
|
-
const from = outer.starts[at];
|
|
1264
|
-
if (from === void 0 || start < from || start + inner.length > from + outer.length) {
|
|
1265
|
-
return false;
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
return true;
|
|
1269
|
-
}
|
|
1270
|
-
function listBeats(occurrences) {
|
|
1271
|
-
const shown = occurrences.slice(0, 5).map((occurrence) => roundTo(occurrence.startBeat, 3));
|
|
1272
|
-
return occurrences.length > shown.length ? `${shown.join(", ")}, and ${occurrences.length - shown.length} more` : shown.join(", ");
|
|
1273
|
-
}
|
|
1274
|
-
function motifFromGroup(group, notes) {
|
|
1275
|
-
const first = _nullishCoalesce(group.starts[0], () => ( 0));
|
|
1276
|
-
const prime = notes.slice(first, first + group.length).map((note) => ({ ...note }));
|
|
1277
|
-
const primePitch = _nullishCoalesce(_optionalChain([prime, 'access', _77 => _77[0], 'optionalAccess', _78 => _78.pitch]), () => ( 0));
|
|
1278
|
-
const primeSpan = onsetSpan(prime);
|
|
1279
|
-
const occurrences = group.starts.map((start) => {
|
|
1280
|
-
const statement = notes.slice(start, start + group.length);
|
|
1281
|
-
const span = onsetSpan(statement);
|
|
1282
|
-
return {
|
|
1283
|
-
noteIndex: start,
|
|
1284
|
-
startBeat: _nullishCoalesce(_optionalChain([statement, 'access', _79 => _79[0], 'optionalAccess', _80 => _80.startBeat]), () => ( 0)),
|
|
1285
|
-
endBeat: endBeatOf(statement),
|
|
1286
|
-
transpose: (_nullishCoalesce(_optionalChain([statement, 'access', _81 => _81[0], 'optionalAccess', _82 => _82.pitch]), () => ( 0))) - primePitch,
|
|
1287
|
-
timeRatio: primeSpan > _chunkS5BKZZGMcjs.BEAT_EPS ? span / primeSpan : 1
|
|
1288
|
-
};
|
|
1289
|
-
});
|
|
1290
|
-
const shape = melodicContour(prime).shape;
|
|
1291
|
-
const transposed = occurrences.some((occurrence) => occurrence.transpose !== 0);
|
|
1292
|
-
const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS);
|
|
1293
|
-
const detail = [
|
|
1294
|
-
transposed ? "at more than one pitch level" : "at the same pitch level",
|
|
1295
|
-
stretched ? "and in more than one set of note values" : ""
|
|
1296
|
-
].filter((part) => part !== "").join(" ");
|
|
1297
|
-
return {
|
|
1298
|
-
notes: prime,
|
|
1299
|
-
intervals: intervalsOf(prime),
|
|
1300
|
-
rhythm: _nullishCoalesce(rhythmProfile(onsetGaps(prime)), () => ( [])),
|
|
1301
|
-
occurrences,
|
|
1302
|
-
rationale: `${group.length}-note ${shape} cell stated ${occurrences.length} ${occurrences.length === 1 ? "time" : "times"} (beats ${listBeats(occurrences)}), ${detail}`
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
function extractMotifs(notes, opts = {}) {
|
|
1306
|
-
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
1307
|
-
const minNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.minNotes, () => ( MIN_CELL_NOTES)), "motif minNotes", 2, 64);
|
|
1308
|
-
const maxNotes = _chunkS5BKZZGMcjs.assertInteger.call(void 0, _nullishCoalesce(asked.maxNotes, () => ( MAX_CELL_NOTES)), "motif maxNotes", 2, 64);
|
|
1309
|
-
if (maxNotes < minNotes) {
|
|
1310
|
-
throw new (0, _chunkS5BKZZGMcjs.InvalidInputError)(
|
|
1311
|
-
`motif maxNotes must be at least minNotes ${minNotes}; received ${maxNotes}`
|
|
1312
|
-
);
|
|
1313
|
-
}
|
|
1314
|
-
const minOccurrences = _chunkS5BKZZGMcjs.assertInteger.call(void 0,
|
|
1315
|
-
_nullishCoalesce(asked.minOccurrences, () => ( MIN_OCCURRENCES)),
|
|
1316
|
-
"motif minOccurrences",
|
|
1317
|
-
2
|
|
1318
|
-
);
|
|
1319
|
-
const sounding = orderedNotes(notes, "melody notes", asked.budget);
|
|
1320
|
-
if (sounding.length < minNotes) {
|
|
1321
|
-
return [];
|
|
1322
|
-
}
|
|
1323
|
-
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0,
|
|
1324
|
-
sounding.length * (maxNotes - minNotes + 1),
|
|
1325
|
-
"motif windows",
|
|
1326
|
-
asked.budget
|
|
1327
|
-
);
|
|
1328
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1329
|
-
for (let length = minNotes; length <= maxNotes; length += 1) {
|
|
1330
|
-
for (let start = 0; start + length <= sounding.length; start += 1) {
|
|
1331
|
-
const signature = windowSignature(sounding.slice(start, start + length));
|
|
1332
|
-
if (signature === null) {
|
|
1333
|
-
continue;
|
|
1334
|
-
}
|
|
1335
|
-
const group = groups.get(signature);
|
|
1336
|
-
if (group === void 0) {
|
|
1337
|
-
groups.set(signature, { signature, length, starts: [start] });
|
|
1338
|
-
} else {
|
|
1339
|
-
group.starts.push(start);
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
const candidates = [...groups.values()].map((group) => ({ ...group, starts: nonOverlapping(group.starts, group.length) })).filter((group) => group.starts.length >= minOccurrences).sort(
|
|
1344
|
-
(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)
|
|
1345
|
-
);
|
|
1346
|
-
const keptByLength = /* @__PURE__ */ new Map();
|
|
1347
|
-
const kept = [];
|
|
1348
|
-
let comparisons = 0;
|
|
1349
|
-
for (const candidate of candidates) {
|
|
1350
|
-
const first = _nullishCoalesce(candidate.starts[0], () => ( 0));
|
|
1351
|
-
let covered = false;
|
|
1352
|
-
for (let length = candidate.length + 1; length <= maxNotes && !covered; length += 1) {
|
|
1353
|
-
const byStart = keptByLength.get(length);
|
|
1354
|
-
if (byStart === void 0) {
|
|
1355
|
-
continue;
|
|
1356
|
-
}
|
|
1357
|
-
for (let from = Math.max(0, first - (length - candidate.length)); from <= first; from += 1) {
|
|
1358
|
-
comparisons += 1;
|
|
1359
|
-
const outer = byStart.get(from);
|
|
1360
|
-
if (outer === void 0 || outer.starts.length < candidate.starts.length) {
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
comparisons += outer.starts.length + candidate.starts.length;
|
|
1364
|
-
if (subsumes(outer, candidate)) {
|
|
1365
|
-
covered = true;
|
|
1366
|
-
break;
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "motif subsumption comparisons", asked.budget);
|
|
1371
|
-
if (!covered) {
|
|
1372
|
-
kept.push(candidate);
|
|
1373
|
-
let byStart = keptByLength.get(candidate.length);
|
|
1374
|
-
if (byStart === void 0) {
|
|
1375
|
-
byStart = /* @__PURE__ */ new Map();
|
|
1376
|
-
keptByLength.set(candidate.length, byStart);
|
|
1377
|
-
}
|
|
1378
|
-
for (const start of candidate.starts) {
|
|
1379
|
-
byStart.set(start, candidate);
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
return kept.map((group) => motifFromGroup(group, sounding));
|
|
1384
|
-
}
|
|
1385
|
-
function motifFromNotes(notes) {
|
|
1386
|
-
const sounding = orderedNotes(notes, "motif notes").map((note) => ({ ...note }));
|
|
1387
|
-
return motifFromGroup(
|
|
1388
|
-
{ signature: _nullishCoalesce(windowSignature(sounding), () => ( "")), length: sounding.length, starts: [0] },
|
|
1389
|
-
sounding
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
// src/analyze/melody/relation.ts
|
|
1394
|
-
function spellingOf(key) {
|
|
1395
|
-
if (key === void 0) {
|
|
1396
|
-
return "sharp";
|
|
1397
|
-
}
|
|
1398
|
-
return _chunk3R3QVKY5cjs.keySignatureFifths.call(void 0, key.tonic, key.scale) < 0 ? "flat" : "sharp";
|
|
1399
|
-
}
|
|
1400
|
-
function intervalBetween(from, to, key) {
|
|
1401
|
-
const spelling = spellingOf(key);
|
|
1402
|
-
return _chunkS5BKZZGMcjs.spelledInterval.call(void 0, _chunkS5BKZZGMcjs.midiToNote.call(void 0, from, spelling), _chunkS5BKZZGMcjs.midiToNote.call(void 0, to, spelling));
|
|
1403
|
-
}
|
|
1404
|
-
function intervalName(interval) {
|
|
1405
|
-
return `${interval.descending && interval.semitones !== 0 ? "-" : ""}${interval.quality}${interval.number}`;
|
|
1406
|
-
}
|
|
1407
|
-
function ladderIndices(notes, key) {
|
|
1408
|
-
const root = _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
1409
|
-
const offsets = _chunk3R3QVKY5cjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, pc - root));
|
|
1410
|
-
if (offsets.length === 0) {
|
|
1411
|
-
return null;
|
|
1412
|
-
}
|
|
1413
|
-
const out = [];
|
|
1414
|
-
for (const note of notes) {
|
|
1415
|
-
const semitones = note.pitch - root;
|
|
1416
|
-
const octave = Math.floor(semitones / 12);
|
|
1417
|
-
const degree = offsets.indexOf(semitones - octave * 12);
|
|
1418
|
-
if (degree < 0) {
|
|
1419
|
-
return null;
|
|
1420
|
-
}
|
|
1421
|
-
out.push(octave * offsets.length + degree);
|
|
1422
|
-
}
|
|
1423
|
-
return out;
|
|
1424
|
-
}
|
|
1425
|
-
function ladderSteps(indices) {
|
|
1426
|
-
const out = [];
|
|
1427
|
-
for (let i = 1; i < indices.length; i += 1) {
|
|
1428
|
-
out.push((_nullishCoalesce(indices[i], () => ( 0))) - (_nullishCoalesce(indices[i - 1], () => ( 0))));
|
|
1429
|
-
}
|
|
1430
|
-
return out;
|
|
1431
|
-
}
|
|
1432
|
-
function pitchShapes(a, b) {
|
|
1433
|
-
const shapes = [];
|
|
1434
|
-
if (sameNumbers(a, b)) {
|
|
1435
|
-
shapes.push("exact");
|
|
1436
|
-
}
|
|
1437
|
-
if (sameNumbers(
|
|
1438
|
-
b,
|
|
1439
|
-
a.map((step) => -step)
|
|
1440
|
-
)) {
|
|
1441
|
-
shapes.push("inversion");
|
|
1442
|
-
}
|
|
1443
|
-
const reversed = [...a].reverse();
|
|
1444
|
-
if (sameNumbers(
|
|
1445
|
-
b,
|
|
1446
|
-
reversed.map((step) => -step)
|
|
1447
|
-
)) {
|
|
1448
|
-
shapes.push("retrograde");
|
|
1449
|
-
}
|
|
1450
|
-
if (sameNumbers(b, reversed)) {
|
|
1451
|
-
shapes.push("retrogradeInversion");
|
|
1452
|
-
}
|
|
1453
|
-
return shapes;
|
|
1454
|
-
}
|
|
1455
|
-
function totalGap(gaps) {
|
|
1456
|
-
return gaps.reduce((sum, gap) => sum + gap, 0);
|
|
1457
|
-
}
|
|
1458
|
-
function stretchRatio(modelGaps, answerGaps) {
|
|
1459
|
-
const span = totalGap(modelGaps);
|
|
1460
|
-
return span > _chunkS5BKZZGMcjs.BEAT_EPS ? totalGap(answerGaps) / span : 1;
|
|
1461
|
-
}
|
|
1462
|
-
function stretchPhrase(ratio) {
|
|
1463
|
-
return `note values ${roundTo(ratio, 3)}x the model's`;
|
|
1464
|
-
}
|
|
1465
|
-
function relateMotifs(a, b, keyLike) {
|
|
1466
|
-
const key = keyLike === void 0 ? void 0 : _chunk3R3QVKY5cjs.resolveKey.call(void 0, keyLike);
|
|
1467
|
-
const model = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, a, "a").notes, "a.notes");
|
|
1468
|
-
const answer = _chunkS5BKZZGMcjs.assertArray.call(void 0, _chunkS5BKZZGMcjs.assertRecord.call(void 0, b, "b").notes, "b.notes");
|
|
1469
|
-
if (model.length === 0 || model.length !== answer.length) {
|
|
1470
|
-
return null;
|
|
1471
|
-
}
|
|
1472
|
-
const modelGaps = onsetGaps(model);
|
|
1473
|
-
const answerGaps = onsetGaps(answer);
|
|
1474
|
-
const modelProfile = rhythmProfile(modelGaps);
|
|
1475
|
-
const answerProfile = rhythmProfile(answerGaps);
|
|
1476
|
-
const retroGaps = retrogradeGaps(model);
|
|
1477
|
-
const retroProfile = rhythmProfile(retroGaps);
|
|
1478
|
-
if (modelProfile === null || answerProfile === null) {
|
|
1479
|
-
return null;
|
|
1480
|
-
}
|
|
1481
|
-
const forward = sameNumbers(modelProfile, answerProfile);
|
|
1482
|
-
const backward = retroProfile !== null && sameNumbers(retroProfile, answerProfile);
|
|
1483
|
-
const semitones = (_nullishCoalesce(_optionalChain([answer, 'access', _83 => _83[0], 'optionalAccess', _84 => _84.pitch]), () => ( 0))) - (_nullishCoalesce(_optionalChain([model, 'access', _85 => _85[0], 'optionalAccess', _86 => _86.pitch]), () => ( 0)));
|
|
1484
|
-
const interval = intervalBetween(_nullishCoalesce(_optionalChain([model, 'access', _87 => _87[0], 'optionalAccess', _88 => _88.pitch]), () => ( 0)), _nullishCoalesce(_optionalChain([answer, 'access', _89 => _89[0], 'optionalAccess', _90 => _90.pitch]), () => ( 0)), key);
|
|
1485
|
-
const gap = (_nullishCoalesce(_optionalChain([answer, 'access', _91 => _91[0], 'optionalAccess', _92 => _92.startBeat]), () => ( 0))) - endBeatOf(model);
|
|
1486
|
-
const sequence = gap >= -_chunkSGLEQVJHcjs.HUMANIZE_ADJACENCY && gap <= _chunkSGLEQVJHcjs.HUMANIZE_ADJACENCY;
|
|
1487
|
-
const timeRatio = stretchRatio(modelGaps, answerGaps);
|
|
1488
|
-
const stretched = Math.abs(timeRatio - 1) > _chunkS5BKZZGMcjs.BEAT_EPS;
|
|
1489
|
-
const shapes = pitchShapes(intervalsOf(model), intervalsOf(answer));
|
|
1490
|
-
const base = { sequence, semitones, interval, timeRatio };
|
|
1491
|
-
const backwardBase = { ...base, timeRatio: stretchRatio(retroGaps, answerGaps) };
|
|
1492
|
-
if (forward && shapes.includes("exact")) {
|
|
1493
|
-
if (semitones === 0) {
|
|
1494
|
-
if (!stretched) {
|
|
1495
|
-
return {
|
|
1496
|
-
...base,
|
|
1497
|
-
kind: "repetition",
|
|
1498
|
-
rationale: "Exact repetition: the same pitches in the same rhythm"
|
|
1499
|
-
};
|
|
1500
|
-
}
|
|
1501
|
-
const augmented = timeRatio > 1;
|
|
1502
|
-
return {
|
|
1503
|
-
...base,
|
|
1504
|
-
kind: augmented ? "augmentation" : "diminution",
|
|
1505
|
-
rationale: `${augmented ? "Augmentation" : "Diminution"}: the same pitches in ${stretchPhrase(timeRatio)}`
|
|
1506
|
-
};
|
|
1507
|
-
}
|
|
1508
|
-
const name = intervalName(interval);
|
|
1509
|
-
return {
|
|
1510
|
-
...base,
|
|
1511
|
-
kind: "transposition",
|
|
1512
|
-
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)}` : "")
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
if (forward && key !== void 0) {
|
|
1516
|
-
const modelLadder = ladderIndices(model, key.scale);
|
|
1517
|
-
const answerLadder = ladderIndices(answer, key.scale);
|
|
1518
|
-
if (modelLadder !== null && answerLadder !== null) {
|
|
1519
|
-
const degrees = (_nullishCoalesce(answerLadder[0], () => ( 0))) - (_nullishCoalesce(modelLadder[0], () => ( 0)));
|
|
1520
|
-
if (degrees !== 0 && sameNumbers(ladderSteps(modelLadder), ladderSteps(answerLadder))) {
|
|
1521
|
-
const direction = degrees > 0 ? "higher" : "lower";
|
|
1522
|
-
const count = Math.abs(degrees);
|
|
1523
|
-
return {
|
|
1524
|
-
...base,
|
|
1525
|
-
kind: "tonalTransposition",
|
|
1526
|
-
degrees,
|
|
1527
|
-
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`
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
if (forward && shapes.includes("inversion")) {
|
|
1533
|
-
return {
|
|
1534
|
-
...base,
|
|
1535
|
-
kind: "inversion",
|
|
1536
|
-
rationale: "Inversion: every interval turned upside down about the first note" + (stretched ? `, in ${stretchPhrase(timeRatio)}` : "")
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
if (backward && shapes.includes("retrograde")) {
|
|
1540
|
-
return {
|
|
1541
|
-
...backwardBase,
|
|
1542
|
-
kind: "retrograde",
|
|
1543
|
-
rationale: "Retrograde: the cell read back to front, rhythm included"
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
if (backward && shapes.includes("retrogradeInversion")) {
|
|
1547
|
-
return {
|
|
1548
|
-
...backwardBase,
|
|
1549
|
-
kind: "retrogradeInversion",
|
|
1550
|
-
rationale: "Retrograde inversion: the cell read back to front and turned upside down"
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
return null;
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
// src/analyze/melody/similarity.ts
|
|
1557
|
-
var INTERVAL_TOLERANCE = 6;
|
|
1558
|
-
var PITCH_WEIGHT = 0.75;
|
|
1559
|
-
var RHYTHM_WEIGHT = 0.25;
|
|
1560
|
-
function gradedEditDistance(a, b, cost) {
|
|
1561
|
-
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
1562
|
-
for (let i = 1; i <= a.length; i += 1) {
|
|
1563
|
-
const row = new Array(b.length + 1);
|
|
1564
|
-
row[0] = i;
|
|
1565
|
-
for (let j = 1; j <= b.length; j += 1) {
|
|
1566
|
-
const substitution = (_nullishCoalesce(previous[j - 1], () => ( 0))) + cost(_nullishCoalesce(a[i - 1], () => ( 0)), _nullishCoalesce(b[j - 1], () => ( 0)));
|
|
1567
|
-
const deletion = (_nullishCoalesce(previous[j], () => ( 0))) + 1;
|
|
1568
|
-
const insertion = (_nullishCoalesce(row[j - 1], () => ( 0))) + 1;
|
|
1569
|
-
row[j] = Math.min(substitution, deletion, insertion);
|
|
1570
|
-
}
|
|
1571
|
-
previous = row;
|
|
1572
|
-
}
|
|
1573
|
-
return _nullishCoalesce(previous[b.length], () => ( 0));
|
|
1574
|
-
}
|
|
1575
|
-
function distanceToSimilarity(distance, a, b) {
|
|
1576
|
-
const longest = Math.max(a, b);
|
|
1577
|
-
if (longest === 0) {
|
|
1578
|
-
return 1;
|
|
1579
|
-
}
|
|
1580
|
-
return Math.min(1, Math.max(0, 1 - distance / longest));
|
|
1581
|
-
}
|
|
1582
|
-
function intervalCost(x, y) {
|
|
1583
|
-
return Math.min(1, Math.abs(x - y) / INTERVAL_TOLERANCE);
|
|
1584
|
-
}
|
|
1585
|
-
function gapCost(x, y) {
|
|
1586
|
-
if (!(x > _chunkS5BKZZGMcjs.BEAT_EPS) || !(y > _chunkS5BKZZGMcjs.BEAT_EPS)) {
|
|
1587
|
-
return Math.abs(x - y) > _chunkS5BKZZGMcjs.BEAT_EPS ? 1 : 0;
|
|
1588
|
-
}
|
|
1589
|
-
return Math.min(1, Math.abs(Math.log2(x / y)));
|
|
1590
|
-
}
|
|
1591
|
-
function compareMelodies(a, b) {
|
|
1592
|
-
const left = orderedNotes(phraseNotes(a), "melody a");
|
|
1593
|
-
const right = orderedNotes(phraseNotes(b), "melody b");
|
|
1594
|
-
const leftIntervals = intervalsOf(left);
|
|
1595
|
-
const rightIntervals = intervalsOf(right);
|
|
1596
|
-
const pitchSimilarity = distanceToSimilarity(
|
|
1597
|
-
gradedEditDistance(leftIntervals, rightIntervals, intervalCost),
|
|
1598
|
-
leftIntervals.length,
|
|
1599
|
-
rightIntervals.length
|
|
1600
|
-
);
|
|
1601
|
-
const leftGaps = _nullishCoalesce(rhythmProfile(onsetGaps(left)), () => ( onsetGaps(left)));
|
|
1602
|
-
const rightGaps = _nullishCoalesce(rhythmProfile(onsetGaps(right)), () => ( onsetGaps(right)));
|
|
1603
|
-
const rhythmSimilarity = distanceToSimilarity(
|
|
1604
|
-
gradedEditDistance(leftGaps, rightGaps, gapCost),
|
|
1605
|
-
leftGaps.length,
|
|
1606
|
-
rightGaps.length
|
|
1607
|
-
);
|
|
1608
|
-
const similarity = PITCH_WEIGHT * pitchSimilarity + RHYTHM_WEIGHT * rhythmSimilarity;
|
|
1609
|
-
return {
|
|
1610
|
-
similarity,
|
|
1611
|
-
pitchSimilarity,
|
|
1612
|
-
rhythmSimilarity,
|
|
1613
|
-
rationale: `Interval shapes ${Math.round(pitchSimilarity * 100)}% alike, rhythms ${Math.round(rhythmSimilarity * 100)}% alike, weighted ${PITCH_WEIGHT}/${RHYTHM_WEIGHT} into ${roundTo(similarity, 3)}`
|
|
1614
|
-
};
|
|
1615
|
-
}
|
|
1616
|
-
function melodicSimilarity(a, b) {
|
|
1617
|
-
return compareMelodies(a, b).similarity;
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
1154
|
// src/analyze/form/phrase.ts
|
|
1621
1155
|
var CADENCE_BOUNDARY_STRENGTH = {
|
|
1622
1156
|
authentic: 0.9,
|
|
@@ -1644,6 +1178,7 @@ var PHRASE_CUT_COST = 1.3;
|
|
|
1644
1178
|
var CLOSING_SHARE = 0.65;
|
|
1645
1179
|
var FINAL_PHRASE_BONUS = 0.15;
|
|
1646
1180
|
var HYPERMETRIC_CLOSE_BONUS = 0.1;
|
|
1181
|
+
var IMPERFECT_CLOSURE_FACTOR = 0.8;
|
|
1647
1182
|
var EVADED_FACTOR = 0.6;
|
|
1648
1183
|
var SIGNAL_ORDER = [
|
|
1649
1184
|
"cadence",
|
|
@@ -1654,7 +1189,7 @@ var SIGNAL_ORDER = [
|
|
|
1654
1189
|
"hypermeter"
|
|
1655
1190
|
];
|
|
1656
1191
|
function addSignal(boundaries, beat, signal, strength, cadence = null) {
|
|
1657
|
-
const key = Math.round(beat /
|
|
1192
|
+
const key = Math.round(beat / _chunkS3QELJUEcjs.BEAT_EPS);
|
|
1658
1193
|
const existing = boundaries.get(key);
|
|
1659
1194
|
const boundary = _nullishCoalesce(existing, () => ( { beat, strengths: /* @__PURE__ */ new Map(), cadence: null }));
|
|
1660
1195
|
boundary.strengths.set(signal, Math.max(_nullishCoalesce(boundary.strengths.get(signal), () => ( 0)), strength));
|
|
@@ -1668,17 +1203,23 @@ function readBoundary(boundary) {
|
|
|
1668
1203
|
(a, b) => b[1] - a[1] || SIGNAL_ORDER.indexOf(a[0]) - SIGNAL_ORDER.indexOf(b[0])
|
|
1669
1204
|
);
|
|
1670
1205
|
return {
|
|
1671
|
-
strength:
|
|
1206
|
+
strength: _chunkLQZQDBCDcjs.combineEvidence.call(void 0, signals.map(([, strength]) => strength)),
|
|
1207
|
+
uncredited: _chunkLQZQDBCDcjs.combineEvidence.call(void 0,
|
|
1208
|
+
signals.filter(([signal]) => signal !== "cadence").map(([, strength]) => strength)
|
|
1209
|
+
),
|
|
1672
1210
|
signals: signals.map(([signal]) => signal)
|
|
1673
1211
|
};
|
|
1674
1212
|
}
|
|
1213
|
+
function closesPhrase(atBeat, startBeat, endBeat) {
|
|
1214
|
+
return atBeat >= startBeat - _chunkS3QELJUEcjs.BEAT_EPS && atBeat < endBeat - _chunkS3QELJUEcjs.BEAT_EPS;
|
|
1215
|
+
}
|
|
1675
1216
|
function lengthFit(length, expected) {
|
|
1676
1217
|
if (!(expected > 0)) {
|
|
1677
1218
|
return 0;
|
|
1678
1219
|
}
|
|
1679
1220
|
return Math.max(-1, 1 - Math.abs(length - expected) / expected);
|
|
1680
1221
|
}
|
|
1681
|
-
function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
|
|
1222
|
+
function choosePhrasePath(beats, strengths, minPhraseBeats, expected, held) {
|
|
1682
1223
|
const value = new Array(beats.length).fill(Number.NEGATIVE_INFINITY);
|
|
1683
1224
|
const cameFrom = new Array(beats.length).fill(-1);
|
|
1684
1225
|
value[0] = 0;
|
|
@@ -1689,10 +1230,12 @@ function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
|
|
|
1689
1230
|
continue;
|
|
1690
1231
|
}
|
|
1691
1232
|
const length = endBeat - (_nullishCoalesce(beats[i], () => ( 0)));
|
|
1692
|
-
if (length < minPhraseBeats -
|
|
1233
|
+
if (length < minPhraseBeats - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1693
1234
|
continue;
|
|
1694
1235
|
}
|
|
1695
|
-
const
|
|
1236
|
+
const credited = held === void 0 || closesPhrase(_nullishCoalesce(held.arrivals[j], () => ( endBeat)), _nullishCoalesce(beats[i], () => ( 0)), endBeat);
|
|
1237
|
+
const strength = credited ? _nullishCoalesce(strengths[j], () => ( 0)) : _nullishCoalesce(held.uncredited[j], () => ( 0));
|
|
1238
|
+
const gain = (_nullishCoalesce(value[i], () => ( 0))) + strength + lengthFit(length, expected) - PHRASE_CUT_COST;
|
|
1696
1239
|
if (gain > (_nullishCoalesce(value[j], () => ( Number.NEGATIVE_INFINITY)))) {
|
|
1697
1240
|
value[j] = gain;
|
|
1698
1241
|
cameFrom[j] = i;
|
|
@@ -1711,44 +1254,47 @@ function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
|
|
|
1711
1254
|
}
|
|
1712
1255
|
return path;
|
|
1713
1256
|
}
|
|
1714
|
-
function
|
|
1715
|
-
|
|
1257
|
+
function boundaryCadences(timeline, key) {
|
|
1258
|
+
const hits = _chunkLQZQDBCDcjs.detectCadences.call(void 0, timeline, key).filter((hit) => hit.cadence.type !== null);
|
|
1259
|
+
const spans = cadenceSpans(
|
|
1260
|
+
hits.map((hit) => ({
|
|
1261
|
+
atBeat: hit.atBeat,
|
|
1262
|
+
strength: CADENCE_BOUNDARY_STRENGTH[hit.cadence.type]
|
|
1263
|
+
})),
|
|
1264
|
+
timeline.segments
|
|
1265
|
+
);
|
|
1266
|
+
return { hits, spans };
|
|
1716
1267
|
}
|
|
1717
|
-
function
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
};
|
|
1725
|
-
const bars = lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);
|
|
1726
|
-
return Math.round(bars * 100) / 100;
|
|
1268
|
+
function keyContextFor(key, timeline, meters, totalBeats, budget) {
|
|
1269
|
+
if (key !== void 0) {
|
|
1270
|
+
return key;
|
|
1271
|
+
}
|
|
1272
|
+
const regions = _chunkLQZQDBCDcjs.detectModulations.call(void 0, timeline.segments, { meters, totalBeats, budget });
|
|
1273
|
+
const keyAt = _chunkLQZQDBCDcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, regions), () => ( _chunkYDGMWH43cjs.resolveKey.call(void 0, _chunkYDGMWH43cjs.majorKey.call(void 0, 0)))));
|
|
1274
|
+
return (beat) => _chunkYDGMWH43cjs.scaleOf.call(void 0, keyAt(beat));
|
|
1727
1275
|
}
|
|
1728
|
-
function
|
|
1729
|
-
|
|
1730
|
-
|
|
1276
|
+
function heldCadenceBoundaries(boundaries, hit, span, meters, spanEnd) {
|
|
1277
|
+
const firstLine = _chunkS3QELJUEcjs.barIndexAt.call(void 0, span.atBeat, meters) + 1;
|
|
1278
|
+
const lastLine = _chunkS3QELJUEcjs.barIndexAt.call(void 0, Math.min(span.endBeat, spanEnd), meters);
|
|
1279
|
+
for (let bar = firstLine; bar <= lastLine; bar += 1) {
|
|
1280
|
+
const line = _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
|
|
1281
|
+
if (line < span.endBeat - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1282
|
+
addSignal(boundaries, line, "cadence", span.strength, hit);
|
|
1283
|
+
}
|
|
1731
1284
|
}
|
|
1732
|
-
const regions = _chunkSGLEQVJHcjs.keyTimelineFromNotes.call(void 0, notes, {
|
|
1733
|
-
meters,
|
|
1734
|
-
totalBeats,
|
|
1735
|
-
budget: opts.budget
|
|
1736
|
-
});
|
|
1737
|
-
const keyAt = _chunkSGLEQVJHcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkSGLEQVJHcjs.prevailingKeyOf.call(void 0, regions), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))));
|
|
1738
|
-
return (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
|
|
1739
1285
|
}
|
|
1740
1286
|
function restBoundaries(boundaries, notes, meters, restBeats) {
|
|
1741
1287
|
const ordered = [...notes].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
|
|
1742
1288
|
let covered = Number.NEGATIVE_INFINITY;
|
|
1743
1289
|
for (const note of ordered) {
|
|
1744
1290
|
const gap = note.startBeat - covered;
|
|
1745
|
-
if (Number.isFinite(covered) && gap >= restBeats -
|
|
1746
|
-
const bar =
|
|
1291
|
+
if (Number.isFinite(covered) && gap >= restBeats - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1292
|
+
const bar = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, note.startBeat, meters);
|
|
1747
1293
|
addSignal(
|
|
1748
1294
|
boundaries,
|
|
1749
1295
|
note.startBeat,
|
|
1750
1296
|
"rest",
|
|
1751
|
-
REST_STRENGTH *
|
|
1297
|
+
REST_STRENGTH * _chunkLQZQDBCDcjs.clamp01.call(void 0, bar > 0 ? gap / bar : 1)
|
|
1752
1298
|
);
|
|
1753
1299
|
}
|
|
1754
1300
|
covered = Math.max(covered, note.startBeat + note.durationBeat);
|
|
@@ -1758,13 +1304,13 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
|
|
|
1758
1304
|
if (!(windowBeats > 0)) {
|
|
1759
1305
|
return;
|
|
1760
1306
|
}
|
|
1761
|
-
const slices =
|
|
1762
|
-
|
|
1763
|
-
const onsetsIn = (from, to) => notes.filter((note) => note.startBeat >= from -
|
|
1307
|
+
const slices = _chunkLQZQDBCDcjs.sliceBars.call(void 0, notes, meters, spanStart, spanEnd, budget);
|
|
1308
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, slices.length * notes.length, "form repetition comparisons", budget);
|
|
1309
|
+
const onsetsIn = (from, to) => notes.filter((note) => note.startBeat >= from - _chunkS3QELJUEcjs.BEAT_EPS && note.startBeat < to - _chunkS3QELJUEcjs.BEAT_EPS);
|
|
1764
1310
|
let comparisons = 0;
|
|
1765
1311
|
for (const slice of slices) {
|
|
1766
1312
|
const beat = slice.startBeat;
|
|
1767
|
-
if (beat - windowBeats < spanStart -
|
|
1313
|
+
if (beat - windowBeats < spanStart - _chunkS3QELJUEcjs.BEAT_EPS || beat + windowBeats > spanEnd + _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1768
1314
|
continue;
|
|
1769
1315
|
}
|
|
1770
1316
|
const before = onsetsIn(beat - windowBeats, beat);
|
|
@@ -1773,8 +1319,8 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
|
|
|
1773
1319
|
continue;
|
|
1774
1320
|
}
|
|
1775
1321
|
comparisons += before.length * after.length;
|
|
1776
|
-
|
|
1777
|
-
const similarity = melodicSimilarity(before, after);
|
|
1322
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, comparisons, "form repetition melody comparisons", budget);
|
|
1323
|
+
const similarity = _chunkLQZQDBCDcjs.melodicSimilarity.call(void 0, before, after);
|
|
1778
1324
|
if (similarity >= REPETITION_THRESHOLD) {
|
|
1779
1325
|
addSignal(boundaries, beat, "repetition", REPETITION_STRENGTH * similarity);
|
|
1780
1326
|
}
|
|
@@ -1790,7 +1336,7 @@ function structuralWeightOf(cadence, confidence, aligned, isLast) {
|
|
|
1790
1336
|
if (result.strength === "perfect") {
|
|
1791
1337
|
base = 1;
|
|
1792
1338
|
} else if (result.strength === "imperfect") {
|
|
1793
|
-
base *=
|
|
1339
|
+
base *= IMPERFECT_CLOSURE_FACTOR;
|
|
1794
1340
|
}
|
|
1795
1341
|
}
|
|
1796
1342
|
if (result.evaded) {
|
|
@@ -1803,7 +1349,7 @@ function structuralWeightOf(cadence, confidence, aligned, isLast) {
|
|
|
1803
1349
|
if (aligned) {
|
|
1804
1350
|
weight += HYPERMETRIC_CLOSE_BONUS;
|
|
1805
1351
|
}
|
|
1806
|
-
return
|
|
1352
|
+
return _chunkLQZQDBCDcjs.clamp01.call(void 0, weight);
|
|
1807
1353
|
}
|
|
1808
1354
|
function describeCadence(cadence) {
|
|
1809
1355
|
if (cadence === null || cadence.cadence.type === null) {
|
|
@@ -1814,72 +1360,61 @@ function describeCadence(cadence) {
|
|
|
1814
1360
|
return `a ${graded} cadence at beat ${cadence.atBeat}`;
|
|
1815
1361
|
}
|
|
1816
1362
|
function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
1817
|
-
|
|
1818
|
-
const meters =
|
|
1819
|
-
|
|
1363
|
+
_chunkLQZQDBCDcjs.assertChordTimeline.call(void 0, timeline);
|
|
1364
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "phrase meters");
|
|
1365
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "phrase notes", {
|
|
1820
1366
|
allowNonPositiveDuration: true,
|
|
1821
1367
|
budget: opts.budget
|
|
1822
1368
|
});
|
|
1823
1369
|
if (opts.hypermeter !== void 0) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1370
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, opts.hypermeter.confidence, 0, 1, "phrase hypermeter confidence");
|
|
1371
|
+
_chunkS3QELJUEcjs.assertPositiveInt.call(void 0, opts.hypermeter.groupBars, "phrase hypermeter groupBars");
|
|
1826
1372
|
}
|
|
1827
1373
|
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
1828
1374
|
const segments = timeline.segments;
|
|
1829
1375
|
const firstSegment = segments[0];
|
|
1830
1376
|
const lastSegment = segments[segments.length - 1];
|
|
1831
|
-
const
|
|
1832
|
-
(
|
|
1833
|
-
_nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess',
|
|
1377
|
+
const earliest = Math.min(
|
|
1378
|
+
_chunkLQZQDBCDcjs.firstSoundingBeat.call(void 0, sounding, Number.POSITIVE_INFINITY),
|
|
1379
|
+
_nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _44 => _44.startBeat]), () => ( Number.POSITIVE_INFINITY))
|
|
1834
1380
|
);
|
|
1835
|
-
const
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess',
|
|
1381
|
+
const firstOnset = Number.isFinite(earliest) ? earliest : 0;
|
|
1382
|
+
const spanStart = _chunkLQZQDBCDcjs.barGridStart.call(void 0, firstOnset, meters);
|
|
1383
|
+
const latestEnd = Math.max(
|
|
1384
|
+
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _45 => _45.endBeat]), () => ( Number.NEGATIVE_INFINITY)),
|
|
1385
|
+
_chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, sounding, Number.NEGATIVE_INFINITY)
|
|
1839
1386
|
);
|
|
1840
|
-
const
|
|
1841
|
-
|
|
1842
|
-
if (spanEnd <= spanStart +
|
|
1387
|
+
const notesEnd = Number.isFinite(latestEnd) ? latestEnd : 0;
|
|
1388
|
+
const spanEnd = _chunkLQZQDBCDcjs.resolveSpanEnd.call(void 0, opts.totalBeats, spanStart, notesEnd, "phrase totalBeats");
|
|
1389
|
+
if (spanEnd <= spanStart + _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1843
1390
|
return [];
|
|
1844
1391
|
}
|
|
1845
|
-
const barBeats =
|
|
1392
|
+
const barBeats = _chunkS3QELJUEcjs.beatsPerBarAt.call(void 0, Math.max(0, spanStart), meters);
|
|
1846
1393
|
const restBeats = _nullishCoalesce(opts.restBeats, () => ( barBeats / 2));
|
|
1847
|
-
|
|
1394
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, restBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase restBeats");
|
|
1848
1395
|
const longNoteBeats = _nullishCoalesce(opts.longNoteBeats, () => ( barBeats));
|
|
1849
|
-
|
|
1396
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, longNoteBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase longNoteBeats");
|
|
1850
1397
|
const minPhraseBeats = _nullishCoalesce(opts.minPhraseBeats, () => ( barBeats));
|
|
1851
|
-
|
|
1852
|
-
const key = keyContextFor(opts,
|
|
1853
|
-
const cadences =
|
|
1854
|
-
const grouping = _nullishCoalesce(opts.hypermeter, () => (
|
|
1855
|
-
|
|
1856
|
-
cadenceBeats: cadences.map((hit) => hit.atBeat),
|
|
1857
|
-
budget: opts.budget
|
|
1858
|
-
})));
|
|
1859
|
-
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, Math.max(0, spanStart), meters);
|
|
1398
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, minPhraseBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase minPhraseBeats");
|
|
1399
|
+
const key = keyContextFor(opts.key, timeline, meters, spanEnd, opts.budget);
|
|
1400
|
+
const { hits: cadences, spans } = boundaryCadences(timeline, key);
|
|
1401
|
+
const grouping = _nullishCoalesce(opts.hypermeter, () => ( hypermeterOver(sounding, meters, { totalBeats: spanEnd, budget: opts.budget }, () => spans)));
|
|
1402
|
+
const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, Math.max(0, spanStart), meters);
|
|
1860
1403
|
const hyperBeats = Math.max(
|
|
1861
1404
|
barBeats,
|
|
1862
|
-
|
|
1405
|
+
_chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar: firstBar + grouping.groupBars, beat: 0 }, meters) - _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar: firstBar, beat: 0 }, meters)
|
|
1863
1406
|
);
|
|
1864
1407
|
const expected = _nullishCoalesce(opts.expectedPhraseBeats, () => ( hyperBeats));
|
|
1865
|
-
|
|
1408
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, expected, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase expectedPhraseBeats");
|
|
1866
1409
|
const boundaries = /* @__PURE__ */ new Map();
|
|
1867
|
-
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
const strength = CADENCE_BOUNDARY_STRENGTH[type];
|
|
1873
|
-
const chordEnd = arrivalEnd(timeline, hit.atBeat);
|
|
1874
|
-
addSignal(boundaries, chordEnd, "cadence", strength, hit);
|
|
1875
|
-
const barEnd = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar: _chunkS5BKZZGMcjs.barIndexAt.call(void 0, hit.atBeat, meters) + 1, beat: 0 }, meters);
|
|
1876
|
-
if (chordEnd > barEnd + _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1877
|
-
addSignal(boundaries, barEnd, "cadence", strength, hit);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1410
|
+
cadences.forEach((hit, index) => {
|
|
1411
|
+
const span = spans[index];
|
|
1412
|
+
addSignal(boundaries, span.endBeat, "cadence", span.strength, hit);
|
|
1413
|
+
heldCadenceBoundaries(boundaries, hit, span, meters, spanEnd);
|
|
1414
|
+
});
|
|
1880
1415
|
restBoundaries(boundaries, sounding, meters, restBeats);
|
|
1881
1416
|
for (const note of sounding) {
|
|
1882
|
-
if (note.durationBeat >= longNoteBeats -
|
|
1417
|
+
if (note.durationBeat >= longNoteBeats - _chunkS3QELJUEcjs.BEAT_EPS) {
|
|
1883
1418
|
addSignal(boundaries, note.startBeat + note.durationBeat, "longNote", LONG_NOTE_STRENGTH);
|
|
1884
1419
|
}
|
|
1885
1420
|
}
|
|
@@ -1889,9 +1424,9 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1889
1424
|
}
|
|
1890
1425
|
addSignal(boundaries, spanEnd, "pieceEnd", PIECE_END_STRENGTH);
|
|
1891
1426
|
const candidates = [...boundaries.values()].filter(
|
|
1892
|
-
(boundary) => boundary.beat > spanStart +
|
|
1427
|
+
(boundary) => boundary.beat > spanStart + _chunkS3QELJUEcjs.BEAT_EPS && boundary.beat < spanEnd - _chunkS3QELJUEcjs.BEAT_EPS
|
|
1893
1428
|
).filter((boundary) => readBoundary(boundary).strength > 0).sort((a, b) => a.beat - b.beat);
|
|
1894
|
-
const endBoundary = _nullishCoalesce(boundaries.get(Math.round(spanEnd /
|
|
1429
|
+
const endBoundary = _nullishCoalesce(boundaries.get(Math.round(spanEnd / _chunkS3QELJUEcjs.BEAT_EPS)), () => ( {
|
|
1895
1430
|
beat: spanEnd,
|
|
1896
1431
|
strengths: /* @__PURE__ */ new Map(),
|
|
1897
1432
|
cadence: null
|
|
@@ -1901,40 +1436,45 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1901
1436
|
...candidates,
|
|
1902
1437
|
endBoundary
|
|
1903
1438
|
];
|
|
1904
|
-
|
|
1439
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, stops.length * stops.length, "form phrase boundary pairs", opts.budget);
|
|
1905
1440
|
const readings = stops.map(readBoundary);
|
|
1906
1441
|
const path = choosePhrasePath(
|
|
1907
1442
|
stops.map((stop) => stop.beat),
|
|
1908
1443
|
readings.map((reading) => reading.strength),
|
|
1909
1444
|
minPhraseBeats,
|
|
1910
|
-
expected
|
|
1445
|
+
expected,
|
|
1446
|
+
{
|
|
1447
|
+
arrivals: stops.map((stop) => _nullishCoalesce(_optionalChain([stop, 'access', _46 => _46.cadence, 'optionalAccess', _47 => _47.atBeat]), () => ( stop.beat))),
|
|
1448
|
+
uncredited: readings.map((reading) => reading.uncredited)
|
|
1449
|
+
}
|
|
1911
1450
|
);
|
|
1912
1451
|
const downbeatKeys = new Set(
|
|
1913
|
-
grouping.downbeats.map((downbeat) => Math.round(downbeat /
|
|
1452
|
+
grouping.downbeats.map((downbeat) => Math.round(downbeat / _chunkS3QELJUEcjs.BEAT_EPS))
|
|
1914
1453
|
);
|
|
1915
1454
|
const phrases = [];
|
|
1916
1455
|
let from = 0;
|
|
1917
1456
|
for (let n = 0; n < path.length; n += 1) {
|
|
1918
1457
|
const to = _nullishCoalesce(path[n], () => ( stops.length - 1));
|
|
1919
|
-
const startBeat = _nullishCoalesce(_optionalChain([stops, 'access',
|
|
1458
|
+
const startBeat = _nullishCoalesce(_optionalChain([stops, 'access', _48 => _48[from], 'optionalAccess', _49 => _49.beat]), () => ( spanStart));
|
|
1920
1459
|
const stop = stops[to];
|
|
1921
|
-
const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess',
|
|
1922
|
-
const reading = _nullishCoalesce(readings[to], () => ( { strength: 0, signals: [] }));
|
|
1460
|
+
const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _50 => _50.beat]), () => ( spanEnd));
|
|
1461
|
+
const reading = _nullishCoalesce(readings[to], () => ( { strength: 0, uncredited: 0, signals: [] }));
|
|
1923
1462
|
const length = endBeat - startBeat;
|
|
1924
|
-
const confidence = _chunkSGLEQVJHcjs.clamp01.call(void 0,
|
|
1925
|
-
CLOSING_SHARE * reading.strength + (1 - CLOSING_SHARE) * Math.max(0, lengthFit(length, expected))
|
|
1926
|
-
);
|
|
1927
1463
|
const isLast = n === path.length - 1;
|
|
1928
|
-
const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess',
|
|
1929
|
-
const cadence = closing !== null && closing.atBeat
|
|
1464
|
+
const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _51 => _51.cadence]), () => ( null));
|
|
1465
|
+
const cadence = closing !== null && closesPhrase(closing.atBeat, startBeat, endBeat) ? closing : null;
|
|
1466
|
+
const closingStrength = cadence === null ? reading.uncredited : reading.strength;
|
|
1467
|
+
const confidence = _chunkLQZQDBCDcjs.clamp01.call(void 0,
|
|
1468
|
+
CLOSING_SHARE * closingStrength + (1 - CLOSING_SHARE) * Math.max(0, lengthFit(length, expected))
|
|
1469
|
+
);
|
|
1930
1470
|
const structuralWeight = structuralWeightOf(
|
|
1931
1471
|
cadence,
|
|
1932
1472
|
confidence,
|
|
1933
|
-
downbeatKeys.has(Math.round(endBeat /
|
|
1473
|
+
downbeatKeys.has(Math.round(endBeat / _chunkS3QELJUEcjs.BEAT_EPS)),
|
|
1934
1474
|
isLast
|
|
1935
1475
|
);
|
|
1936
1476
|
const signals = cadence === null ? reading.signals.filter((signal) => signal !== "cadence") : reading.signals;
|
|
1937
|
-
const bars = barSpanOf(startBeat, endBeat, meters);
|
|
1477
|
+
const bars = Math.round(_chunkLQZQDBCDcjs.barSpanOf.call(void 0, startBeat, endBeat, meters) * 100) / 100;
|
|
1938
1478
|
phrases.push({
|
|
1939
1479
|
startBeat,
|
|
1940
1480
|
endBeat,
|
|
@@ -1949,10 +1489,10 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1949
1489
|
return phrases;
|
|
1950
1490
|
}
|
|
1951
1491
|
function structuralCadences(phrases) {
|
|
1952
|
-
|
|
1492
|
+
_chunkS3QELJUEcjs.assertArray.call(void 0, phrases, "phrases");
|
|
1953
1493
|
const ranked = [];
|
|
1954
1494
|
for (let index = 0; index < phrases.length; index += 1) {
|
|
1955
|
-
const phrase =
|
|
1495
|
+
const phrase = _chunkS3QELJUEcjs.assertRecord.call(void 0, phrases[index], `phrases[${index}]`);
|
|
1956
1496
|
if (phrase.cadence === null || phrase.cadence === void 0 || phrase.cadence.cadence.type === null) {
|
|
1957
1497
|
continue;
|
|
1958
1498
|
}
|
|
@@ -1986,50 +1526,55 @@ function isSilent(unit) {
|
|
|
1986
1526
|
return unit.notes.length === 0;
|
|
1987
1527
|
}
|
|
1988
1528
|
function unitSimilarity(a, b) {
|
|
1989
|
-
const harmony =
|
|
1529
|
+
const harmony = _chunkLQZQDBCDcjs.weightSimilarity.call(void 0, a.weights, b.weights);
|
|
1990
1530
|
if (a.notes.length < 2 || b.notes.length < 2) {
|
|
1991
1531
|
return a.notes.length === b.notes.length ? harmony : 0;
|
|
1992
1532
|
}
|
|
1993
|
-
const melody = melodicSimilarity(a.notes, b.notes);
|
|
1994
|
-
return
|
|
1533
|
+
const melody = _chunkLQZQDBCDcjs.melodicSimilarity.call(void 0, a.notes, b.notes);
|
|
1534
|
+
return _chunkLQZQDBCDcjs.clamp01.call(void 0, MELODY_SHARE * melody + HARMONY_SHARE * harmony);
|
|
1995
1535
|
}
|
|
1996
1536
|
function sectionsFromNotes(notes, opts = {}) {
|
|
1997
|
-
const meters =
|
|
1998
|
-
|
|
1537
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, opts, "section meters");
|
|
1538
|
+
_chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "section notes", {
|
|
1999
1539
|
allowNonPositiveDuration: true,
|
|
2000
1540
|
budget: opts.budget
|
|
2001
1541
|
});
|
|
2002
1542
|
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
1543
|
+
const firstSounding = _chunkLQZQDBCDcjs.firstSoundingBeat.call(void 0, sounding);
|
|
1544
|
+
const firstOnset = _chunkLQZQDBCDcjs.barGridStart.call(void 0, firstSounding, meters);
|
|
1545
|
+
const spanEnd = _chunkLQZQDBCDcjs.resolveSpanEnd.call(void 0,
|
|
1546
|
+
opts.totalBeats,
|
|
1547
|
+
firstOnset,
|
|
1548
|
+
_chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, sounding),
|
|
1549
|
+
"section totalBeats"
|
|
1550
|
+
);
|
|
2003
1551
|
if (sounding.length === 0) {
|
|
2004
1552
|
return [];
|
|
2005
1553
|
}
|
|
2006
|
-
const firstOnset = _chunkSGLEQVJHcjs.barGridStart.call(void 0, _chunkSGLEQVJHcjs.firstSoundingBeat.call(void 0, sounding), meters);
|
|
2007
|
-
const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
2008
|
-
const spanEnd = Math.max(firstOnset, _nullishCoalesce(opts.totalBeats, () => ( lastEnd)));
|
|
2009
|
-
_chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "section totalBeats");
|
|
2010
1554
|
const threshold = _nullishCoalesce(opts.threshold, () => ( DEFAULT_THRESHOLD));
|
|
2011
|
-
|
|
1555
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, threshold, 0, 1, "section threshold");
|
|
2012
1556
|
const unitBars = _nullishCoalesce(opts.unitBars, () => ( hypermeter(sounding, meters, { totalBeats: spanEnd, budget: opts.budget }).groupBars));
|
|
2013
|
-
|
|
2014
|
-
const firstBar =
|
|
2015
|
-
const lastBar =
|
|
1557
|
+
_chunkS3QELJUEcjs.assertPositiveInt.call(void 0, unitBars, "section unitBars");
|
|
1558
|
+
const firstBar = _chunkS3QELJUEcjs.barIndexAt.call(void 0, firstOnset, meters);
|
|
1559
|
+
const lastBar = _chunkLQZQDBCDcjs.lastBarOf.call(void 0, meters, firstOnset, spanEnd);
|
|
2016
1560
|
const startBar = Math.max(0, firstBar);
|
|
2017
1561
|
const unitCount = Math.max(1, Math.ceil((lastBar - startBar + 1) / unitBars));
|
|
2018
|
-
|
|
2019
|
-
|
|
1562
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, unitCount * unitCount, "form section comparisons", opts.budget);
|
|
1563
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, unitCount * sounding.length, "form section unit notes", opts.budget);
|
|
2020
1564
|
const units = [];
|
|
2021
1565
|
for (let index = 0; index < unitCount; index += 1) {
|
|
2022
1566
|
const barStart = startBar + index * unitBars;
|
|
2023
|
-
const startBeat = index === 0 ? firstOnset :
|
|
2024
|
-
const endBeat = index === unitCount - 1 ? spanEnd :
|
|
1567
|
+
const startBeat = index === 0 ? firstOnset : _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar: barStart, beat: 0 }, meters);
|
|
1568
|
+
const endBeat = index === unitCount - 1 ? spanEnd : _chunkS3QELJUEcjs.barPositionToBeat.call(void 0, { bar: barStart + unitBars, beat: 0 }, meters);
|
|
1569
|
+
const fromBeat = index === 0 ? Math.min(firstSounding, startBeat) : startBeat;
|
|
2025
1570
|
const unitNotes = sounding.filter(
|
|
2026
|
-
(note) => note.startBeat >=
|
|
1571
|
+
(note) => note.startBeat >= fromBeat - _chunkS3QELJUEcjs.BEAT_EPS && note.startBeat < endBeat - _chunkS3QELJUEcjs.BEAT_EPS
|
|
2027
1572
|
).sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);
|
|
2028
1573
|
units.push({
|
|
2029
1574
|
startBeat,
|
|
2030
1575
|
endBeat,
|
|
2031
1576
|
notes: unitNotes,
|
|
2032
|
-
weights:
|
|
1577
|
+
weights: _chunkLQZQDBCDcjs.windowWeights.call(void 0, unitNotes, startBeat, endBeat, meters).weights
|
|
2033
1578
|
});
|
|
2034
1579
|
}
|
|
2035
1580
|
let comparisons = 0;
|
|
@@ -2038,7 +1583,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2038
1583
|
comparisons += unit.notes.length * earlierNotes;
|
|
2039
1584
|
earlierNotes += unit.notes.length;
|
|
2040
1585
|
}
|
|
2041
|
-
|
|
1586
|
+
_chunkS3QELJUEcjs.assertGenerationBudget.call(void 0, comparisons, "form section melody comparisons", opts.budget);
|
|
2042
1587
|
const labels = [];
|
|
2043
1588
|
const scores = [];
|
|
2044
1589
|
let distinct = 0;
|
|
@@ -2093,8 +1638,8 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2093
1638
|
while (end < units.length && labels[end] === null) {
|
|
2094
1639
|
end += 1;
|
|
2095
1640
|
}
|
|
2096
|
-
const startBeat = _nullishCoalesce(_optionalChain([units, 'access',
|
|
2097
|
-
const endBeat = _nullishCoalesce(_optionalChain([units, 'access',
|
|
1641
|
+
const startBeat = _nullishCoalesce(_optionalChain([units, 'access', _52 => _52[index], 'optionalAccess', _53 => _53.startBeat]), () => ( 0));
|
|
1642
|
+
const endBeat = _nullishCoalesce(_optionalChain([units, 'access', _54 => _54[end - 1], 'optionalAccess', _55 => _55.endBeat]), () => ( spanEnd));
|
|
2098
1643
|
const sectionFirstBar = startBar + index * unitBars;
|
|
2099
1644
|
const sectionLastBar = Math.max(
|
|
2100
1645
|
sectionFirstBar,
|
|
@@ -2105,7 +1650,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2105
1650
|
if (first === void 0) {
|
|
2106
1651
|
firstByLabel.set(label, sectionIndex);
|
|
2107
1652
|
}
|
|
2108
|
-
const similarity = first === void 0 ? 1 :
|
|
1653
|
+
const similarity = first === void 0 ? 1 : _chunkLQZQDBCDcjs.clamp01.call(void 0, scoreSum / Math.max(1, sounded));
|
|
2109
1654
|
sections.push({
|
|
2110
1655
|
label,
|
|
2111
1656
|
startBeat,
|
|
@@ -2113,163 +1658,280 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2113
1658
|
bars: sectionLastBar - sectionFirstBar + 1,
|
|
2114
1659
|
firstOccurrence: _nullishCoalesce(first, () => ( sectionIndex)),
|
|
2115
1660
|
similarity,
|
|
2116
|
-
rationale: first === void 0 ? `Section ${label}: material first heard at beat ${startBeat}` : `Section ${label}: ${Math.round(similarity * 100)}% alike the ${label} at beat ${_nullishCoalesce(_optionalChain([sections, 'access',
|
|
1661
|
+
rationale: first === void 0 ? `Section ${label}: material first heard at beat ${startBeat}` : `Section ${label}: ${Math.round(similarity * 100)}% alike the ${label} at beat ${_nullishCoalesce(_optionalChain([sections, 'access', _56 => _56[first], 'optionalAccess', _57 => _57.startBeat]), () => ( 0))}`
|
|
2117
1662
|
});
|
|
2118
1663
|
index = end;
|
|
2119
1664
|
}
|
|
2120
1665
|
return sections;
|
|
2121
1666
|
}
|
|
2122
1667
|
|
|
2123
|
-
// src/analyze/
|
|
2124
|
-
var
|
|
2125
|
-
function
|
|
2126
|
-
|
|
1668
|
+
// src/analyze/reference/compare.ts
|
|
1669
|
+
var RATIONALE_FIELDS = 2;
|
|
1670
|
+
function rationaleOf(comparison) {
|
|
1671
|
+
const measured = [];
|
|
1672
|
+
for (const group of [comparison.form, comparison.harmony, comparison.melody, comparison.rhythm]) {
|
|
1673
|
+
for (const [name, value] of Object.entries(group)) {
|
|
1674
|
+
if (value !== null) {
|
|
1675
|
+
measured.push({ name, value, order: measured.length });
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
if (measured.length === 0) {
|
|
1680
|
+
return "Nothing to compare: neither profile has material for any field";
|
|
1681
|
+
}
|
|
1682
|
+
const describe = (entries) => entries.map((entry) => `${entry.name} ${Math.round(entry.value * 100)}%`).join(", ");
|
|
1683
|
+
const highest = [...measured].sort((x, y) => y.value - x.value || x.order - y.order);
|
|
1684
|
+
const lowest = [...measured].sort((x, y) => x.value - y.value || x.order - y.order);
|
|
1685
|
+
return `Most alike: ${describe(highest.slice(0, RATIONALE_FIELDS))}; least alike: ${describe(lowest.slice(0, RATIONALE_FIELDS))}`;
|
|
1686
|
+
}
|
|
1687
|
+
function compareReferences(a, b, opts) {
|
|
1688
|
+
_chunkLQZQDBCDcjs.assertReferenceProfile.call(void 0, a, "reference profile a");
|
|
1689
|
+
_chunkLQZQDBCDcjs.assertReferenceProfile.call(void 0, b, "reference profile b");
|
|
1690
|
+
const { budget } = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "compare options");
|
|
1691
|
+
const structural = (profile) => profile.harmony.chords.filter((chord) => chord.level === "structural");
|
|
1692
|
+
const cadences = (profile) => profile.form.phrases.map((phrase) => _nullishCoalesce(_optionalChain([phrase, 'access', _58 => _58.cadence, 'optionalAccess', _59 => _59.type]), () => ( null)));
|
|
1693
|
+
const phraseMelodies = (profile) => profile.form.phrases.map((phrase) => phrase.melody);
|
|
1694
|
+
const comparison = {
|
|
1695
|
+
form: {
|
|
1696
|
+
sectionSequenceSimilarity: _chunkLQZQDBCDcjs.sectionSequenceSimilarity.call(void 0,
|
|
1697
|
+
a.form.sections.map((section) => section.label),
|
|
1698
|
+
b.form.sections.map((section) => section.label)
|
|
1699
|
+
),
|
|
1700
|
+
phraseLengthSimilarity: _chunkLQZQDBCDcjs.phraseLengthSimilarity.call(void 0,
|
|
1701
|
+
a.form.phrases.map((phrase) => phrase.bars),
|
|
1702
|
+
b.form.phrases.map((phrase) => phrase.bars)
|
|
1703
|
+
)
|
|
1704
|
+
},
|
|
1705
|
+
harmony: {
|
|
1706
|
+
progressionSimilarity: _chunkLQZQDBCDcjs.progressionSimilarity.call(void 0, structural(a), structural(b), budget),
|
|
1707
|
+
functionSimilarity: _chunkLQZQDBCDcjs.functionSimilarity.call(void 0, a.harmony.chords, b.harmony.chords),
|
|
1708
|
+
cadenceSimilarity: _chunkLQZQDBCDcjs.cadenceSimilarity.call(void 0, cadences(a), cadences(b)),
|
|
1709
|
+
keyPlanSimilarity: _chunkLQZQDBCDcjs.keyPlanSimilarity.call(void 0,
|
|
1710
|
+
a.harmony.keys.map((region) => region.key),
|
|
1711
|
+
b.harmony.keys.map((region) => region.key)
|
|
1712
|
+
),
|
|
1713
|
+
harmonicRhythmOnsetSimilarity: _chunkLQZQDBCDcjs.onsetSimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm),
|
|
1714
|
+
harmonicRhythmDensitySimilarity: _chunkLQZQDBCDcjs.densitySimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm)
|
|
1715
|
+
},
|
|
1716
|
+
melody: {
|
|
1717
|
+
surfaceSimilarity: _chunkLQZQDBCDcjs.surfaceSimilarity.call(void 0, a.melody.motifs, b.melody.motifs),
|
|
1718
|
+
motifStructureSimilarity: _chunkLQZQDBCDcjs.motifStructureSimilarity.call(void 0,
|
|
1719
|
+
{ graph: a.melody.graph, spanBeats: a.span.endBeat - a.span.startBeat },
|
|
1720
|
+
{ graph: b.melody.graph, spanBeats: b.span.endBeat - b.span.startBeat }
|
|
1721
|
+
),
|
|
1722
|
+
contourSimilarity: _chunkLQZQDBCDcjs.contourSimilarity.call(void 0, phraseMelodies(a), phraseMelodies(b)),
|
|
1723
|
+
registerSimilarity: _chunkLQZQDBCDcjs.registerSimilarity.call(void 0,
|
|
1724
|
+
{ phrases: phraseMelodies(a), mean: _nullishCoalesce(_optionalChain([a, 'access', _60 => _60.melody, 'access', _61 => _61.register, 'optionalAccess', _62 => _62.mean]), () => ( null)) },
|
|
1725
|
+
{ phrases: phraseMelodies(b), mean: _nullishCoalesce(_optionalChain([b, 'access', _63 => _63.melody, 'access', _64 => _64.register, 'optionalAccess', _65 => _65.mean]), () => ( null)) }
|
|
1726
|
+
)
|
|
1727
|
+
},
|
|
1728
|
+
rhythm: {
|
|
1729
|
+
onsetSimilarity: _chunkLQZQDBCDcjs.onsetSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1730
|
+
densitySimilarity: _chunkLQZQDBCDcjs.densitySimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1731
|
+
durationSimilarity: _chunkLQZQDBCDcjs.durationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1732
|
+
syncopationSimilarity: _chunkLQZQDBCDcjs.syncopationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm)
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
1735
|
+
return { ...comparison, rationale: rationaleOf(comparison) };
|
|
2127
1736
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
1737
|
+
|
|
1738
|
+
// src/analyze/reference/profile.ts
|
|
1739
|
+
function spanOf(sounding, timeline, meters, totalBeats) {
|
|
1740
|
+
const segments = timeline.segments;
|
|
1741
|
+
const firstSegment = segments[0];
|
|
1742
|
+
const lastSegment = segments[segments.length - 1];
|
|
1743
|
+
const firstOnset = _chunkLQZQDBCDcjs.firstSoundingBeat.call(void 0, sounding, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _66 => _66.startBeat]), () => ( 0)));
|
|
1744
|
+
const startBeat = _chunkLQZQDBCDcjs.barGridStart.call(void 0,
|
|
1745
|
+
Math.min(firstOnset, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _67 => _67.startBeat]), () => ( firstOnset))),
|
|
1746
|
+
meters
|
|
1747
|
+
);
|
|
1748
|
+
const latestEnd = Math.max(
|
|
1749
|
+
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _68 => _68.endBeat]), () => ( Number.NEGATIVE_INFINITY)),
|
|
1750
|
+
_chunkLQZQDBCDcjs.latestSoundingEnd.call(void 0, sounding, Number.NEGATIVE_INFINITY)
|
|
1751
|
+
);
|
|
1752
|
+
const notesEnd = Number.isFinite(latestEnd) ? latestEnd : 0;
|
|
1753
|
+
return { startBeat, endBeat: Math.max(startBeat, _nullishCoalesce(totalBeats, () => ( notesEnd))) };
|
|
2131
1754
|
}
|
|
2132
|
-
function
|
|
2133
|
-
return
|
|
1755
|
+
function sameKey(a, b) {
|
|
1756
|
+
return _chunkS3QELJUEcjs.pitchClassOf.call(void 0, a.scale.rootPc) === _chunkS3QELJUEcjs.pitchClassOf.call(void 0, b.scale.rootPc) && a.scale.modeMask12 === b.scale.modeMask12;
|
|
2134
1757
|
}
|
|
2135
|
-
function
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
1758
|
+
function keyIndexAt(regions, beat, prevailing) {
|
|
1759
|
+
const holding = regions.findIndex((region) => region.startBeat <= beat && beat < region.endBeat);
|
|
1760
|
+
if (holding >= 0) {
|
|
1761
|
+
return holding;
|
|
1762
|
+
}
|
|
1763
|
+
return Math.max(
|
|
1764
|
+
0,
|
|
1765
|
+
regions.findIndex((region) => sameKey(region.key, prevailing))
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
function sectionAt(sections, beat) {
|
|
1769
|
+
const index = sections.findIndex(
|
|
1770
|
+
(section) => beat >= section.startBeat - _chunkS3QELJUEcjs.BEAT_EPS && beat < section.endBeat - _chunkS3QELJUEcjs.BEAT_EPS
|
|
1771
|
+
);
|
|
1772
|
+
return index < 0 ? null : index;
|
|
1773
|
+
}
|
|
1774
|
+
function readingsFor(notes, opts = {}) {
|
|
1775
|
+
const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "reference options");
|
|
1776
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, asked, "reference meters");
|
|
1777
|
+
const budget = asked.budget;
|
|
1778
|
+
if (asked.timeline === void 0) {
|
|
1779
|
+
return _chunkLQZQDBCDcjs.chordTimelineFromNotes.call(void 0, notes, {
|
|
1780
|
+
meters,
|
|
1781
|
+
key: asked.key,
|
|
1782
|
+
totalBeats: asked.totalBeats,
|
|
1783
|
+
budget
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
const timeline = _chunkLQZQDBCDcjs.assertChordTimeline.call(void 0, asked.timeline, "reference timeline");
|
|
1787
|
+
let keys;
|
|
1788
|
+
if (asked.key === void 0) {
|
|
1789
|
+
keys = _chunkLQZQDBCDcjs.keyTimelineFromNotes.call(void 0, notes, { meters, totalBeats: asked.totalBeats, budget });
|
|
1790
|
+
} else {
|
|
1791
|
+
const sounding = _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
|
|
1792
|
+
allowNonPositiveDuration: true,
|
|
1793
|
+
budget
|
|
1794
|
+
}).filter((note) => note.durationBeat > 0);
|
|
1795
|
+
const span = spanOf(sounding, timeline, meters, asked.totalBeats);
|
|
1796
|
+
keys = _chunkLQZQDBCDcjs.statedKeyRegions.call(void 0, _chunkYDGMWH43cjs.resolveKey.call(void 0, asked.key), span.startBeat, span.endBeat);
|
|
1797
|
+
}
|
|
1798
|
+
return { timeline, keys, prevailingKey: _nullishCoalesce(_chunkLQZQDBCDcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunkYDGMWH43cjs.resolveKey.call(void 0, _chunkYDGMWH43cjs.majorKey.call(void 0, 0)))) };
|
|
1799
|
+
}
|
|
1800
|
+
function referenceFromReadings(notes, readings, opts = {}) {
|
|
1801
|
+
const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, opts, "reference options");
|
|
1802
|
+
const meters = _chunkS3QELJUEcjs.resolveMeters.call(void 0, asked, "reference meters");
|
|
1803
|
+
const budget = asked.budget;
|
|
1804
|
+
const sounding = _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
|
|
1805
|
+
allowNonPositiveDuration: true,
|
|
1806
|
+
budget
|
|
1807
|
+
}).filter((note) => note.durationBeat > 0);
|
|
1808
|
+
if (asked.totalBeats !== void 0) {
|
|
1809
|
+
_chunkS3QELJUEcjs.assertRange.call(void 0, asked.totalBeats, 0, Number.MAX_SAFE_INTEGER, "reference totalBeats");
|
|
1810
|
+
}
|
|
1811
|
+
const line = _chunkLQZQDBCDcjs.orderedNotes.call(void 0, _nullishCoalesce(asked.melody, () => ( notes)), "reference melody", budget).map(
|
|
1812
|
+
(note) => Object.is(note.pitch, -0) ? { ...note, pitch: 0 } : note
|
|
1813
|
+
);
|
|
1814
|
+
const { timeline, prevailingKey } = readings;
|
|
1815
|
+
const span = spanOf(sounding, timeline, meters, asked.totalBeats);
|
|
1816
|
+
const spanEnd = span.endBeat;
|
|
1817
|
+
const keyAt = _chunkLQZQDBCDcjs.keyLookup.call(void 0, readings.keys, prevailingKey);
|
|
1818
|
+
const scaleAt = (beat) => _chunkYDGMWH43cjs.scaleOf.call(void 0, keyAt(beat));
|
|
1819
|
+
const cadenceBeats = _chunkLQZQDBCDcjs.detectCadences.call(void 0, timeline, scaleAt).map((hit) => hit.atBeat);
|
|
1820
|
+
const grouping = hypermeter(sounding, meters, { cadenceBeats, totalBeats: spanEnd, budget });
|
|
1821
|
+
const phrases = phrasesFromTimeline(timeline, notes, {
|
|
1822
|
+
meters,
|
|
1823
|
+
key: scaleAt,
|
|
1824
|
+
hypermeter: grouping,
|
|
1825
|
+
totalBeats: spanEnd,
|
|
1826
|
+
budget
|
|
2140
1827
|
});
|
|
2141
|
-
const
|
|
2142
|
-
for (
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
continue;
|
|
2151
|
-
}
|
|
2152
|
-
const skipped = _nullishCoalesce(rankOf[index], () => ( 0));
|
|
2153
|
-
const at = (position) => _nullishCoalesce(durations[_nullishCoalesce(order[position < skipped ? position : position + 1], () => ( 0))], () => ( 0));
|
|
2154
|
-
const middle = remaining >> 1;
|
|
2155
|
-
floors.push(remaining % 2 === 1 ? at(middle) : (at(middle - 1) + at(middle)) / 2);
|
|
2156
|
-
}
|
|
2157
|
-
return floors;
|
|
2158
|
-
}
|
|
2159
|
-
function isTonicChord(chord, key) {
|
|
2160
|
-
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 0 && !_chunkSGLEQVJHcjs.isAppliedDominant.call(void 0, chord, key);
|
|
2161
|
-
}
|
|
2162
|
-
function functionalReason(segment, before, after, keyAt) {
|
|
2163
|
-
const key = keyAt(segment.startBeat);
|
|
2164
|
-
if (after !== void 0 && _chunkSGLEQVJHcjs.isCadentialSixFour.call(void 0, segment.chord, after.chord, key)) {
|
|
2165
|
-
return { kind: "cadentialSixFour" };
|
|
2166
|
-
}
|
|
2167
|
-
if (isTonicChord(segment.chord, key)) {
|
|
2168
|
-
return { kind: "tonic" };
|
|
2169
|
-
}
|
|
2170
|
-
if (_chunk3R3QVKY5cjs.isDominantChordOf.call(void 0, segment.chord, key)) {
|
|
2171
|
-
return { kind: "dominant" };
|
|
2172
|
-
}
|
|
2173
|
-
if (before !== void 0) {
|
|
2174
|
-
const arrival = _chunkSGLEQVJHcjs.detectCadence.call(void 0, before.chord, segment.chord, key).type;
|
|
2175
|
-
if (arrival !== null) {
|
|
2176
|
-
return { kind: "cadenceArrival", cadence: arrival };
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
if (after !== void 0) {
|
|
2180
|
-
const agent = _chunkSGLEQVJHcjs.detectCadence.call(void 0, segment.chord, after.chord, keyAt(after.startBeat)).type;
|
|
2181
|
-
if (agent !== null && TONIC_ARRIVING.has(agent)) {
|
|
2182
|
-
return { kind: "cadenceAgent", cadence: agent };
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
return null;
|
|
2186
|
-
}
|
|
2187
|
-
function figureReason(segment, before, after) {
|
|
2188
|
-
if (before === void 0 || after === void 0) {
|
|
2189
|
-
return null;
|
|
2190
|
-
}
|
|
2191
|
-
const incoming = signedStep(before.chord.rootPc, segment.chord.rootPc);
|
|
2192
|
-
const outgoing = signedStep(segment.chord.rootPc, after.chord.rootPc);
|
|
2193
|
-
if (!isStep(incoming) || !isStep(outgoing)) {
|
|
2194
|
-
return null;
|
|
2195
|
-
}
|
|
2196
|
-
if (incoming > 0 === outgoing > 0) {
|
|
2197
|
-
return { kind: "passing" };
|
|
2198
|
-
}
|
|
2199
|
-
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, before.chord.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, after.chord.rootPc) ? { kind: "neighbor" } : null;
|
|
2200
|
-
}
|
|
2201
|
-
function describe(reason) {
|
|
2202
|
-
switch (reason.kind) {
|
|
2203
|
-
case "tonic":
|
|
2204
|
-
return "Structural: the tonic of the key, which the progression is heard against";
|
|
2205
|
-
case "dominant":
|
|
2206
|
-
return "Structural: the dominant of the key, the chord its tonic is approached from";
|
|
2207
|
-
case "cadentialSixFour":
|
|
2208
|
-
return "Structural: a tonic six-four on the bass of the dominant that follows, which prolongs that dominant rather than framing the tonic";
|
|
2209
|
-
case "cadenceArrival":
|
|
2210
|
-
return `Structural: the arrival of a ${reason.cadence} cadence`;
|
|
2211
|
-
case "cadenceAgent":
|
|
2212
|
-
return `Structural: the chord that drives a ${reason.cadence} cadence`;
|
|
2213
|
-
case "salient":
|
|
2214
|
-
return "Structural: it holds the harmony longer than the chords around it";
|
|
2215
|
-
case "first":
|
|
2216
|
-
return "Structural: the first chord of the progression frames it";
|
|
2217
|
-
case "last":
|
|
2218
|
-
return "Structural: the last chord of the progression frames it";
|
|
2219
|
-
case "unfigured":
|
|
2220
|
-
return "Structural: its root neither steps through to the next chord nor returns to the last, so it embellishes nothing";
|
|
2221
|
-
case "passing":
|
|
2222
|
-
return "Passing: its root steps in from the previous chord and on to the next in the same direction";
|
|
2223
|
-
case "neighbor":
|
|
2224
|
-
return "Neighbor: its root steps away from the surrounding harmony and returns to it";
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
var BASIS_READINGS = {
|
|
2228
|
-
function: ({ segment, before, after, keyAt }) => functionalReason(segment, before, after, keyAt),
|
|
2229
|
-
duration: ({ segment, salienceFloor }) => segment.endBeat - segment.startBeat > salienceFloor ? { kind: "salient" } : null
|
|
2230
|
-
};
|
|
2231
|
-
var REDUCTION_BASES = Object.keys(BASIS_READINGS);
|
|
2232
|
-
function levelOf(reason) {
|
|
2233
|
-
if (reason.kind === "passing") {
|
|
2234
|
-
return "passing";
|
|
2235
|
-
}
|
|
2236
|
-
return reason.kind === "neighbor" ? "neighbor" : "structural";
|
|
2237
|
-
}
|
|
2238
|
-
function reduceProgression(timeline, key, opts = {}) {
|
|
2239
|
-
_chunkSGLEQVJHcjs.assertChordTimeline.call(void 0, timeline);
|
|
2240
|
-
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "opts");
|
|
2241
|
-
const keyAt = _chunkSGLEQVJHcjs.keyScaleAt.call(void 0, key);
|
|
2242
|
-
const basis = _chunkS5BKZZGMcjs.assertOneOf.call(void 0, _nullishCoalesce(asked.basis, () => ( "function")), REDUCTION_BASES, "reduction basis");
|
|
2243
|
-
const reading = BASIS_READINGS[basis];
|
|
2244
|
-
const segments = timeline.segments;
|
|
2245
|
-
const floors = basis === "duration" ? salienceFloors(segments.map((segment) => segment.endBeat - segment.startBeat)) : [];
|
|
2246
|
-
const result = [];
|
|
2247
|
-
for (let index = 0; index < segments.length; index += 1) {
|
|
2248
|
-
const segment = segments[index];
|
|
2249
|
-
if (segment === void 0) {
|
|
2250
|
-
continue;
|
|
1828
|
+
const cadences = /* @__PURE__ */ new Map();
|
|
1829
|
+
for (const closing of structuralCadences(phrases)) {
|
|
1830
|
+
const type = closing.cadence.type;
|
|
1831
|
+
if (type !== null) {
|
|
1832
|
+
cadences.set(closing.phraseIndex, {
|
|
1833
|
+
atBeat: closing.atBeat,
|
|
1834
|
+
type,
|
|
1835
|
+
weight: closing.weight
|
|
1836
|
+
});
|
|
2251
1837
|
}
|
|
2252
|
-
const previous = segments[index - 1];
|
|
2253
|
-
const following = segments[index + 1];
|
|
2254
|
-
const before = previous !== void 0 && touches(previous, segment) ? previous : void 0;
|
|
2255
|
-
const after = following !== void 0 && touches(segment, following) ? following : void 0;
|
|
2256
|
-
let reason = reading({ segment, before, after, keyAt, salienceFloor: _nullishCoalesce(floors[index], () => ( 0)) });
|
|
2257
|
-
if (reason === null && index === 0) {
|
|
2258
|
-
reason = { kind: "first" };
|
|
2259
|
-
}
|
|
2260
|
-
if (reason === null && index === segments.length - 1) {
|
|
2261
|
-
reason = { kind: "last" };
|
|
2262
|
-
}
|
|
2263
|
-
reason ??= _nullishCoalesce(figureReason(segment, before, after), () => ( { kind: "unfigured" }));
|
|
2264
|
-
result.push({
|
|
2265
|
-
chord: segment.chord,
|
|
2266
|
-
startBeat: segment.startBeat,
|
|
2267
|
-
endBeat: segment.endBeat,
|
|
2268
|
-
level: levelOf(reason),
|
|
2269
|
-
rationale: describe(reason)
|
|
2270
|
-
});
|
|
2271
1838
|
}
|
|
2272
|
-
|
|
1839
|
+
const sections = sectionsFromNotes(notes, {
|
|
1840
|
+
meters,
|
|
1841
|
+
unitBars: grouping.groupBars,
|
|
1842
|
+
totalBeats: spanEnd,
|
|
1843
|
+
budget
|
|
1844
|
+
});
|
|
1845
|
+
const keys = readings.keys.length === 0 && timeline.segments.length > 0 ? [
|
|
1846
|
+
{
|
|
1847
|
+
startBeat: span.startBeat,
|
|
1848
|
+
endBeat: spanEnd,
|
|
1849
|
+
key: _chunkYDGMWH43cjs.resolveKey.call(void 0, prevailingKey),
|
|
1850
|
+
confidence: 0
|
|
1851
|
+
}
|
|
1852
|
+
] : readings.keys.map((region) => ({
|
|
1853
|
+
startBeat: region.startBeat,
|
|
1854
|
+
endBeat: region.endBeat,
|
|
1855
|
+
key: _chunkYDGMWH43cjs.resolveKey.call(void 0, region.key),
|
|
1856
|
+
confidence: region.confidence
|
|
1857
|
+
}));
|
|
1858
|
+
const chords = _chunkLQZQDBCDcjs.reduceProgression.call(void 0, timeline, scaleAt).map((reduced) => {
|
|
1859
|
+
const index = keyIndexAt(keys, (reduced.startBeat + reduced.endBeat) / 2, prevailingKey);
|
|
1860
|
+
const key = keys[index].key;
|
|
1861
|
+
return {
|
|
1862
|
+
startBeat: reduced.startBeat,
|
|
1863
|
+
endBeat: reduced.endBeat,
|
|
1864
|
+
key: index,
|
|
1865
|
+
roman: _chunkLQZQDBCDcjs.chordToRoman.call(void 0, reduced.chord, key),
|
|
1866
|
+
function: _chunkLQZQDBCDcjs.functionOf.call(void 0, reduced.chord, key),
|
|
1867
|
+
level: reduced.level
|
|
1868
|
+
};
|
|
1869
|
+
});
|
|
1870
|
+
const chordOnsets = timeline.segments.map((segment) => ({
|
|
1871
|
+
pitch: 60,
|
|
1872
|
+
startBeat: segment.startBeat,
|
|
1873
|
+
durationBeat: segment.endBeat - segment.startBeat
|
|
1874
|
+
}));
|
|
1875
|
+
const harmonyRhythm = _chunkLQZQDBCDcjs.analyzeRhythm.call(void 0, chordOnsets, { meters, totalBeats: spanEnd, budget });
|
|
1876
|
+
const motifs = _chunkLQZQDBCDcjs.extractMotifs.call(void 0, line, { budget });
|
|
1877
|
+
const graph = _chunkLQZQDBCDcjs.motifGraph.call(void 0, line, motifs, { key: prevailingKey, budget });
|
|
1878
|
+
let register = null;
|
|
1879
|
+
if (line.length > 0) {
|
|
1880
|
+
const pitches = line.map((note) => note.pitch);
|
|
1881
|
+
register = {
|
|
1882
|
+
low: Math.min(...pitches),
|
|
1883
|
+
high: Math.max(...pitches),
|
|
1884
|
+
mean: _chunkLQZQDBCDcjs.weightedMean.call(void 0, line)
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
const lineMean = _nullishCoalesce(_optionalChain([register, 'optionalAccess', _69 => _69.mean]), () => ( 0));
|
|
1888
|
+
const referenceMotifs = motifs.map((motif) => {
|
|
1889
|
+
const first = motif.occurrences[0];
|
|
1890
|
+
return {
|
|
1891
|
+
intervals: motif.intervals,
|
|
1892
|
+
rhythm: motif.rhythm,
|
|
1893
|
+
unitBeats: motif.notes.length < 2 ? 0 : motif.notes[1].startBeat - motif.notes[0].startBeat,
|
|
1894
|
+
spanBeats: first === void 0 ? 0 : first.endBeat - first.startBeat,
|
|
1895
|
+
occurrences: motif.occurrences.length
|
|
1896
|
+
};
|
|
1897
|
+
});
|
|
1898
|
+
const referencePhrases = phrases.map((phrase, index) => ({
|
|
1899
|
+
startBeat: phrase.startBeat,
|
|
1900
|
+
endBeat: phrase.endBeat,
|
|
1901
|
+
bars: _chunkLQZQDBCDcjs.barSpanOf.call(void 0, phrase.startBeat, phrase.endBeat, meters),
|
|
1902
|
+
section: sectionAt(sections, phrase.startBeat),
|
|
1903
|
+
confidence: phrase.confidence,
|
|
1904
|
+
cadence: _nullishCoalesce(cadences.get(index), () => ( null)),
|
|
1905
|
+
melody: _chunkLQZQDBCDcjs.phraseMelody.call(void 0, line, phrase.startBeat, phrase.endBeat, lineMean, meters, budget),
|
|
1906
|
+
motifNodes: graph.nodes.flatMap(
|
|
1907
|
+
(node, nodeIndex) => node.startBeat >= phrase.startBeat - _chunkS3QELJUEcjs.BEAT_EPS && node.startBeat < phrase.endBeat - _chunkS3QELJUEcjs.BEAT_EPS ? [nodeIndex] : []
|
|
1908
|
+
)
|
|
1909
|
+
}));
|
|
1910
|
+
return {
|
|
1911
|
+
profileVersion: _chunkLQZQDBCDcjs.REFERENCE_PROFILE_VERSION,
|
|
1912
|
+
span: {
|
|
1913
|
+
startBeat: span.startBeat,
|
|
1914
|
+
endBeat: spanEnd,
|
|
1915
|
+
bars: _chunkLQZQDBCDcjs.barSpanOf.call(void 0, span.startBeat, spanEnd, meters)
|
|
1916
|
+
},
|
|
1917
|
+
meters,
|
|
1918
|
+
form: {
|
|
1919
|
+
hypermeter: { groupBars: grouping.groupBars, confidence: grouping.confidence },
|
|
1920
|
+
sections,
|
|
1921
|
+
phrases: referencePhrases
|
|
1922
|
+
},
|
|
1923
|
+
harmony: { keys, chords, rhythm: harmonyRhythm },
|
|
1924
|
+
melody: {
|
|
1925
|
+
register,
|
|
1926
|
+
shape: line.length < 2 ? null : _chunkLQZQDBCDcjs.melodicContour.call(void 0, line).shape,
|
|
1927
|
+
motifs: referenceMotifs,
|
|
1928
|
+
graph,
|
|
1929
|
+
rhythm: _chunkLQZQDBCDcjs.rhythmOver.call(void 0, line, { meters, totalBeats: spanEnd, budget }, span.startBeat)
|
|
1930
|
+
}
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
function analyzeReference(notes, opts) {
|
|
1934
|
+
return referenceFromReadings(notes, readingsFor(notes, opts), opts);
|
|
2273
1935
|
}
|
|
2274
1936
|
|
|
2275
1937
|
|
|
@@ -2290,5 +1952,7 @@ function reduceProgression(timeline, key, opts = {}) {
|
|
|
2290
1952
|
|
|
2291
1953
|
|
|
2292
1954
|
|
|
2293
|
-
|
|
2294
|
-
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
exports.trackRoleOf = trackRoleOf; exports.arrangementProfile = arrangementProfile; exports.assertTrackNotes = assertTrackNotes; exports.harmonyTrackSet = harmonyTrackSet; exports.analyzePolyphony = analyzePolyphony; exports.analyzeArrangement = analyzeArrangement; exports.createArrangementSession = createArrangementSession; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.hypermeter = hypermeter; exports.hypermeterOver = hypermeterOver; exports.boundaryCadences = boundaryCadences; exports.keyContextFor = keyContextFor; exports.phrasesFromTimeline = phrasesFromTimeline; exports.structuralCadences = structuralCadences; exports.sectionsFromNotes = sectionsFromNotes; exports.compareReferences = compareReferences; exports.referenceFromReadings = referenceFromReadings; exports.analyzeReference = analyzeReference;
|
|
1958
|
+
//# sourceMappingURL=chunk-3MDOEFIQ.cjs.map
|