@libraz/libcantus 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/README_ja.md +4 -0
- package/dist/analyze/index.cjs +28 -4
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +151 -5
- package/dist/analyze/index.d.ts +151 -5
- package/dist/analyze/index.js +34 -10
- package/dist/{chunk-DSMJRXCZ.cjs → chunk-4R2LWV7E.cjs} +384 -107
- package/dist/chunk-4R2LWV7E.cjs.map +1 -0
- package/dist/{chunk-J7FHVZII.cjs → chunk-B2PW6EHN.cjs} +2000 -30
- package/dist/chunk-B2PW6EHN.cjs.map +1 -0
- package/dist/{chunk-Q6LFSAKV.js → chunk-CPMB7P75.js} +1973 -3
- package/dist/chunk-CPMB7P75.js.map +1 -0
- package/dist/{chunk-H2KJHBW4.js → chunk-GNVN4PPJ.js} +1871 -5
- package/dist/chunk-GNVN4PPJ.js.map +1 -0
- package/dist/{chunk-SGLEQVJH.cjs → chunk-IFZDWIKM.cjs} +1868 -2
- package/dist/chunk-IFZDWIKM.cjs.map +1 -0
- package/dist/{chunk-FBH75DAY.js → chunk-IPEF6NWB.js} +287 -10
- package/dist/chunk-IPEF6NWB.js.map +1 -0
- package/dist/{chunk-HYJJ2V2I.js → chunk-MLBPXA4Z.js} +300 -720
- package/dist/chunk-MLBPXA4Z.js.map +1 -0
- package/dist/{chunk-HWHGOVO6.cjs → chunk-PWC4CRWT.cjs} +1 -1
- package/dist/{chunk-HWHGOVO6.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
- package/dist/{chunk-HPVSH7SF.cjs → chunk-YFA7X2F6.cjs} +350 -770
- package/dist/chunk-YFA7X2F6.cjs.map +1 -0
- package/dist/{chunk-NFJ4P6ZZ.js → chunk-ZBUROTJQ.js} +1 -1
- package/dist/generate/index.cjs +15 -3
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +84 -6
- package/dist/generate/index.d.ts +84 -6
- package/dist/generate/index.js +14 -2
- package/dist/{index-BNSM-DSk.d.cts → index-CY-3NVfy.d.cts} +484 -3
- package/dist/{index-BJgP97N1.d.ts → index-zaZdmnU4.d.ts} +484 -3
- package/dist/index.cjs +43 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +48 -12
- package/dist/model/index.cjs +5 -5
- package/dist/model/index.d.cts +203 -7
- package/dist/model/index.d.ts +203 -7
- package/dist/model/index.js +4 -4
- package/dist/{index-DGCNKAUr.d.cts → profile-CQMEqdH9.d.cts} +65 -598
- package/dist/{index-BRMbPkHE.d.ts → profile-DLdlIpS9.d.ts} +65 -598
- package/dist/{index-BBWNZS0f.d.cts → types-D-XgxGvp.d.cts} +1169 -13
- package/dist/{index-CJBBiAmU.d.ts → types-Dh7C064C.d.ts} +1169 -13
- package/package.json +5 -5
- 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-HYJJ2V2I.js.map +0 -1
- package/dist/chunk-J7FHVZII.cjs.map +0 -1
- package/dist/chunk-Q6LFSAKV.js.map +0 -1
- package/dist/chunk-SGLEQVJH.cjs.map +0 -1
- /package/dist/{chunk-NFJ4P6ZZ.js.map → chunk-ZBUROTJQ.js.map} +0 -0
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
var _chunkSGLEQVJHcjs = require('./chunk-SGLEQVJH.cjs');
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
|
|
@@ -41,9 +40,27 @@ var _chunkSGLEQVJHcjs = require('./chunk-SGLEQVJH.cjs');
|
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
|
|
44
|
-
var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
|
|
45
43
|
|
|
46
44
|
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
var _chunkIFZDWIKMcjs = require('./chunk-IFZDWIKM.cjs');
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
var _chunk3R3QVKY5cjs = require('./chunk-3R3QVKY5.cjs');
|
|
47
64
|
|
|
48
65
|
|
|
49
66
|
|
|
@@ -108,7 +125,7 @@ function assertTrackNotes(tracks, options) {
|
|
|
108
125
|
}
|
|
109
126
|
function assertGivenTimeline(timeline, name) {
|
|
110
127
|
if (timeline !== void 0) {
|
|
111
|
-
|
|
128
|
+
_chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, timeline, name);
|
|
112
129
|
}
|
|
113
130
|
}
|
|
114
131
|
function assertGivenKeys(keys, name) {
|
|
@@ -195,7 +212,7 @@ function splitIntoSubVoices(ordered) {
|
|
|
195
212
|
continue;
|
|
196
213
|
}
|
|
197
214
|
const end = last.startBeat + last.durationBeat;
|
|
198
|
-
if (!
|
|
215
|
+
if (!_chunkIFZDWIKMcjs.hasEnded.call(void 0, end, note.startBeat)) {
|
|
199
216
|
continue;
|
|
200
217
|
}
|
|
201
218
|
const distance = Math.abs(last.pitch - note.pitch);
|
|
@@ -216,13 +233,13 @@ function splitIntoSubVoices(ordered) {
|
|
|
216
233
|
for (let start = 0; start < ordered.length; ) {
|
|
217
234
|
const onset = _nullishCoalesce(_optionalChain([ordered, 'access', _13 => _13[start], 'optionalAccess', _14 => _14.startBeat]), () => ( 0));
|
|
218
235
|
let end = start + 1;
|
|
219
|
-
while (end < ordered.length &&
|
|
236
|
+
while (end < ordered.length && _chunkIFZDWIKMcjs.sameInstant.call(void 0, _nullishCoalesce(_optionalChain([ordered, 'access', _15 => _15[end], 'optionalAccess', _16 => _16.startBeat]), () => ( 0)), onset)) {
|
|
220
237
|
end += 1;
|
|
221
238
|
}
|
|
222
239
|
const block = ordered.slice(start, end);
|
|
223
240
|
if (block.length > 1) {
|
|
224
241
|
const freeLanes = lanes.map((lane, index) => ({ index, last: lane[lane.length - 1] })).filter(
|
|
225
|
-
(entry) => entry.last !== void 0 &&
|
|
242
|
+
(entry) => entry.last !== void 0 && _chunkIFZDWIKMcjs.hasEnded.call(void 0, entry.last.startBeat + entry.last.durationBeat, onset)
|
|
226
243
|
).sort((a, b) => a.last.pitch - b.last.pitch);
|
|
227
244
|
const assignment = pairWithFreeLanes(block, freeLanes);
|
|
228
245
|
const taken = new Set(
|
|
@@ -326,7 +343,7 @@ function prepareTracks(tracks) {
|
|
|
326
343
|
const sounding = voice.map((note, i) => {
|
|
327
344
|
const prev = i > 0 ? voice[i - 1] : void 0;
|
|
328
345
|
const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
|
|
329
|
-
const contiguous = prevEnd !== void 0 &&
|
|
346
|
+
const contiguous = prevEnd !== void 0 && _chunkIFZDWIKMcjs.adjacent.call(void 0, prevEnd, note.startBeat);
|
|
330
347
|
return {
|
|
331
348
|
id: note.id,
|
|
332
349
|
originalIndex: note.originalIndex,
|
|
@@ -359,7 +376,7 @@ function withGivenKeys(result, given) {
|
|
|
359
376
|
if (given === void 0) {
|
|
360
377
|
return result;
|
|
361
378
|
}
|
|
362
|
-
return { ...result, keys: given, prevailingKey: _nullishCoalesce(
|
|
379
|
+
return { ...result, keys: given, prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, given), () => ( result.prevailingKey)) };
|
|
363
380
|
}
|
|
364
381
|
function callerTimeline(timeline, key, given, pooled, meters, budget) {
|
|
365
382
|
const chartEnd = timeline.segments.reduce((end, segment) => Math.max(end, segment.endBeat), 0);
|
|
@@ -368,15 +385,15 @@ function callerTimeline(timeline, key, given, pooled, meters, budget) {
|
|
|
368
385
|
0
|
|
369
386
|
);
|
|
370
387
|
const totalBeats = Math.max(chartEnd, soundingEnd);
|
|
371
|
-
const { startBeat: musicStart } =
|
|
372
|
-
const keys = _nullishCoalesce(given, () => (
|
|
373
|
-
key !== void 0 ? [{ startBeat: musicStart, endBeat: totalBeats, key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, key), confidence: 1 }] :
|
|
388
|
+
const { startBeat: musicStart } = _chunkIFZDWIKMcjs.gridForNotes.call(void 0, pooled, _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, 0, meters));
|
|
389
|
+
const keys = _nullishCoalesce(given, () => ( _chunkIFZDWIKMcjs.attachPivots.call(void 0,
|
|
390
|
+
key !== void 0 ? [{ startBeat: musicStart, endBeat: totalBeats, key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, key), confidence: 1 }] : _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }),
|
|
374
391
|
timeline.segments
|
|
375
392
|
)));
|
|
376
393
|
return {
|
|
377
394
|
timeline,
|
|
378
395
|
keys,
|
|
379
|
-
prevailingKey: _nullishCoalesce(
|
|
396
|
+
prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))),
|
|
380
397
|
segmentConfidence: timeline.segments.map(() => 0)
|
|
381
398
|
};
|
|
382
399
|
}
|
|
@@ -393,7 +410,7 @@ function soundingVoicesAt(prepared, beat) {
|
|
|
393
410
|
continue;
|
|
394
411
|
}
|
|
395
412
|
const snap = { pitch: note.pitch };
|
|
396
|
-
const previous =
|
|
413
|
+
const previous = _chunkIFZDWIKMcjs.sameInstant.call(void 0, note.startBeat, beat) ? note.prevPitch : note.pitch;
|
|
397
414
|
if (previous !== void 0) {
|
|
398
415
|
snap.prevPitch = previous;
|
|
399
416
|
}
|
|
@@ -437,7 +454,7 @@ function analyzePolyphony(notes, chordAtBeat, key) {
|
|
|
437
454
|
if (subVoice === void 0) {
|
|
438
455
|
continue;
|
|
439
456
|
}
|
|
440
|
-
const analyzed =
|
|
457
|
+
const analyzed = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0,
|
|
441
458
|
subVoice.voice,
|
|
442
459
|
chordAtBeat,
|
|
443
460
|
key,
|
|
@@ -455,7 +472,7 @@ function analyzePolyphony(notes, chordAtBeat, key) {
|
|
|
455
472
|
if (analyzed !== void 0) {
|
|
456
473
|
return analyzed;
|
|
457
474
|
}
|
|
458
|
-
const [only] =
|
|
475
|
+
const [only] = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0, [{ ...note, id: index }], chordAtBeat, key);
|
|
459
476
|
return _nullishCoalesce(only, () => ( {
|
|
460
477
|
noteId: index,
|
|
461
478
|
pitch: note.pitch,
|
|
@@ -522,7 +539,7 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
522
539
|
let evidence;
|
|
523
540
|
let inferred;
|
|
524
541
|
if (opts.timeline === void 0) {
|
|
525
|
-
const run =
|
|
542
|
+
const run = _chunkIFZDWIKMcjs.analyzeTimeline.call(void 0,
|
|
526
543
|
pooled,
|
|
527
544
|
{
|
|
528
545
|
key: opts.key,
|
|
@@ -540,9 +557,9 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
540
557
|
inferred = callerTimeline(opts.timeline, opts.key, opts.keys, pooled, meters, budget);
|
|
541
558
|
}
|
|
542
559
|
const { timeline, keys, prevailingKey, segmentConfidence } = inferred;
|
|
543
|
-
const keyAt =
|
|
560
|
+
const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, keys, prevailingKey);
|
|
544
561
|
const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
|
|
545
|
-
const cadences =
|
|
562
|
+
const cadences = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, scaleAt);
|
|
546
563
|
const soundingCache = /* @__PURE__ */ new Map();
|
|
547
564
|
const trackAnalyses = [];
|
|
548
565
|
const conflicts = [];
|
|
@@ -561,7 +578,7 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
561
578
|
if (!subVoice) {
|
|
562
579
|
continue;
|
|
563
580
|
}
|
|
564
|
-
const analyzed =
|
|
581
|
+
const analyzed = _chunkIFZDWIKMcjs.analyzeVoice.call(void 0,
|
|
565
582
|
subVoice.voice,
|
|
566
583
|
timeline.at,
|
|
567
584
|
scaleAt,
|
|
@@ -578,7 +595,7 @@ function analyzeArrangementWith(tracks, opts = {}, previous, dirty) {
|
|
|
578
595
|
continue;
|
|
579
596
|
}
|
|
580
597
|
for (const beat of evaluationBeats(note, timeline)) {
|
|
581
|
-
const atOnset =
|
|
598
|
+
const atOnset = _chunkIFZDWIKMcjs.sameInstant.call(void 0, beat, note.startBeat);
|
|
582
599
|
const others = otherVoicesSounding(prepared, t, v, beat, soundingCache);
|
|
583
600
|
const safetyQuery = {
|
|
584
601
|
profile,
|
|
@@ -735,7 +752,7 @@ function sessionOf(tracks, opts, analysis, evidence) {
|
|
|
735
752
|
if (edited === null && !reordered) {
|
|
736
753
|
return sessionOf(next, opts, analysis, evidence);
|
|
737
754
|
}
|
|
738
|
-
const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } :
|
|
755
|
+
const dirty = evidence === void 0 ? void 0 : edited === null ? { from: 0, to: 0 } : _chunkIFZDWIKMcjs.dirtySlotsFor.call(void 0, evidence, edited.startBeat, edited.endBeat);
|
|
739
756
|
const run = analyzeArrangementWith(next, opts, evidence, dirty);
|
|
740
757
|
return sessionOf(next, opts, run.analysis, run.evidence);
|
|
741
758
|
}
|
|
@@ -788,7 +805,7 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
788
805
|
const pooled = poolNotes(tracks, harmonyTracks);
|
|
789
806
|
const all = poolNotes(tracks);
|
|
790
807
|
const totalBeats = all.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
791
|
-
const { origin: sampleStart, startBeat: musicStart } =
|
|
808
|
+
const { origin: sampleStart, startBeat: musicStart } = _chunkIFZDWIKMcjs.gridForNotes.call(void 0, all, step);
|
|
792
809
|
const suppliedKeys = opts.timeline === void 0 ? void 0 : _nullishCoalesce(opts.keys, () => ( (opts.key !== void 0 ? [
|
|
793
810
|
{
|
|
794
811
|
// The one region a named key holds starts where the music does,
|
|
@@ -799,8 +816,8 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
799
816
|
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, opts.key),
|
|
800
817
|
confidence: 1
|
|
801
818
|
}
|
|
802
|
-
] :
|
|
803
|
-
const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ?
|
|
819
|
+
] : _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, pooled, { meters, totalBeats, budget }))));
|
|
820
|
+
const { timeline, keys, prevailingKey } = opts.timeline === void 0 || suppliedKeys === void 0 ? _chunkIFZDWIKMcjs.chordTimelineFromNotes.call(void 0, pooled, {
|
|
804
821
|
key: opts.key,
|
|
805
822
|
meters,
|
|
806
823
|
pickupBeats: opts.pickupBeats,
|
|
@@ -809,10 +826,10 @@ function tensionCurve(tracks, opts = {}) {
|
|
|
809
826
|
}) : {
|
|
810
827
|
timeline: opts.timeline,
|
|
811
828
|
keys: suppliedKeys,
|
|
812
|
-
prevailingKey: _nullishCoalesce(
|
|
829
|
+
prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, suppliedKeys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0))))
|
|
813
830
|
};
|
|
814
831
|
const regions = _nullishCoalesce(opts.keys, () => ( keys));
|
|
815
|
-
const keyAt =
|
|
832
|
+
const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, regions), () => ( prevailingKey)));
|
|
816
833
|
const prepared = prepareTracks(tracks);
|
|
817
834
|
const points = [];
|
|
818
835
|
const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step - _chunkS5BKZZGMcjs.BEAT_EPS));
|
|
@@ -870,7 +887,7 @@ function sampleTension(prepared, timeline, key, meters, profile, beat) {
|
|
|
870
887
|
return 0;
|
|
871
888
|
}
|
|
872
889
|
const chord = timeline.at(beat);
|
|
873
|
-
const functionScore = chord ? FUNCTION_TENSION[
|
|
890
|
+
const functionScore = chord ? FUNCTION_TENSION[_chunkIFZDWIKMcjs.functionOf.call(void 0, chord, key)] : 0;
|
|
874
891
|
const chordPcs = chord ? new Set(_chunk3R3QVKY5cjs.chordPitchClasses.call(void 0, chord)) : null;
|
|
875
892
|
const strongBeat = _chunkS5BKZZGMcjs.isStrongBeat.call(void 0, beat, meters);
|
|
876
893
|
let nonChord = 0;
|
|
@@ -950,7 +967,7 @@ function accentContrast(novelty, slices, groupBars, phase) {
|
|
|
950
967
|
continue;
|
|
951
968
|
}
|
|
952
969
|
const value = _nullishCoalesce(novelty[i], () => ( 0));
|
|
953
|
-
if (
|
|
970
|
+
if (_chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - phase, groupBars) === 0) {
|
|
954
971
|
headSum += value;
|
|
955
972
|
headCount += 1;
|
|
956
973
|
} else {
|
|
@@ -968,7 +985,7 @@ function cadenceFitOf(cadenceBars, slices, groupBars, phase) {
|
|
|
968
985
|
let ends = 0;
|
|
969
986
|
let fit = 0;
|
|
970
987
|
for (const slice of slices) {
|
|
971
|
-
if (slice.index < 0 ||
|
|
988
|
+
if (slice.index < 0 || _chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - phase, groupBars) !== groupBars - 1) {
|
|
972
989
|
continue;
|
|
973
990
|
}
|
|
974
991
|
ends += 1;
|
|
@@ -978,7 +995,7 @@ function cadenceFitOf(cadenceBars, slices, groupBars, phase) {
|
|
|
978
995
|
fit += ELIDED_FIT;
|
|
979
996
|
}
|
|
980
997
|
}
|
|
981
|
-
return ends === 0 ? 0 :
|
|
998
|
+
return ends === 0 ? 0 : _chunkIFZDWIKMcjs.clamp01.call(void 0, fit / ends);
|
|
982
999
|
}
|
|
983
1000
|
function undecidedReading(slices) {
|
|
984
1001
|
const fullBars = slices.filter((slice) => slice.index >= 0);
|
|
@@ -999,12 +1016,12 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
999
1016
|
budget: asked.budget
|
|
1000
1017
|
});
|
|
1001
1018
|
const sounding = notes.filter((note) => note.durationBeat > 0);
|
|
1002
|
-
const firstOnset =
|
|
1019
|
+
const firstOnset = _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding);
|
|
1003
1020
|
const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
1004
1021
|
const spanEnd = _nullishCoalesce(asked.totalBeats, () => ( lastEnd));
|
|
1005
1022
|
_chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "hypermeter totalBeats");
|
|
1006
|
-
const spanStart =
|
|
1007
|
-
const slices =
|
|
1023
|
+
const spanStart = _chunkIFZDWIKMcjs.barGridStart.call(void 0, firstOnset, meters);
|
|
1024
|
+
const slices = _chunkIFZDWIKMcjs.sliceBars.call(void 0, sounding, meters, spanStart, spanEnd, asked.budget);
|
|
1008
1025
|
const fullBars = slices.reduce((count, slice) => slice.index >= 0 ? count + 1 : count, 0);
|
|
1009
1026
|
const candidates = GROUP_CANDIDATES.filter((groupBars) => fullBars >= groupBars * MIN_GROUPS);
|
|
1010
1027
|
if (candidates.length === 0) {
|
|
@@ -1015,7 +1032,7 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1015
1032
|
"hypermeter readings",
|
|
1016
1033
|
asked.budget
|
|
1017
1034
|
);
|
|
1018
|
-
const novelty = slices.map((_, index) =>
|
|
1035
|
+
const novelty = slices.map((_, index) => _chunkIFZDWIKMcjs.harmonicNovelty.call(void 0, slices, index));
|
|
1019
1036
|
const cadenceBars = new Set((_nullishCoalesce(asked.cadenceBeats, () => ( []))).map((beat) => _chunkS5BKZZGMcjs.barIndexAt.call(void 0, beat, meters)));
|
|
1020
1037
|
const hasCadences = cadenceBars.size > 0;
|
|
1021
1038
|
const readings = [];
|
|
@@ -1030,14 +1047,14 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1030
1047
|
if (hasCadences && cadenceFit < bestFit - _chunkS5BKZZGMcjs.BEAT_EPS) {
|
|
1031
1048
|
continue;
|
|
1032
1049
|
}
|
|
1033
|
-
const evidence =
|
|
1050
|
+
const evidence = _chunkIFZDWIKMcjs.clamp01.call(void 0,
|
|
1034
1051
|
hasCadences ? (
|
|
1035
1052
|
// Floored at zero rather than summed raw: group heads that restate
|
|
1036
1053
|
// the harmony their group opened with — the commonest way a phrase
|
|
1037
1054
|
// begins — change less than the bars between them, and a negative
|
|
1038
1055
|
// contrast subtracted from the cadence term would count that
|
|
1039
1056
|
// restatement as evidence against the very phase it confirms.
|
|
1040
|
-
CONTRAST_SHARE *
|
|
1057
|
+
CONTRAST_SHARE * _chunkIFZDWIKMcjs.clamp01.call(void 0, contrast) + CADENCE_SHARE * cadenceFit
|
|
1041
1058
|
) : contrast
|
|
1042
1059
|
);
|
|
1043
1060
|
readings.push({
|
|
@@ -1058,8 +1075,8 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1058
1075
|
return undecidedReading(slices);
|
|
1059
1076
|
}
|
|
1060
1077
|
const runnerUp = readings.find((reading) => reading.groupBars !== best.groupBars);
|
|
1061
|
-
const margin = runnerUp === void 0 || best.score <= _chunkS5BKZZGMcjs.BEAT_EPS ? 1 :
|
|
1062
|
-
const downbeats = slices.filter((slice) => slice.index >= 0 &&
|
|
1078
|
+
const margin = runnerUp === void 0 || best.score <= _chunkS5BKZZGMcjs.BEAT_EPS ? 1 : _chunkIFZDWIKMcjs.clamp01.call(void 0, (best.score - runnerUp.score) / best.score);
|
|
1079
|
+
const downbeats = slices.filter((slice) => slice.index >= 0 && _chunkIFZDWIKMcjs.floorMod.call(void 0, slice.index - best.phase, best.groupBars) === 0).map((slice) => slice.startBeat);
|
|
1063
1080
|
const cadencePart = hasCadences ? `${Math.round(best.cadenceFit * 100)}% of group ends closed by a cadence` : "no cadences given";
|
|
1064
1081
|
return {
|
|
1065
1082
|
groupBars: best.groupBars,
|
|
@@ -1069,554 +1086,11 @@ function hypermeter(notes, meter, opts = {}) {
|
|
|
1069
1086
|
// runner-up is nearly always a multiple or a divisor of the winner — four
|
|
1070
1087
|
// bars nest inside eight — and a reading of the same accents at another
|
|
1071
1088
|
// level is weaker counter-evidence than an unrelated rival would be.
|
|
1072
|
-
confidence:
|
|
1089
|
+
confidence: _chunkIFZDWIKMcjs.clamp01.call(void 0, best.evidence * (MARGIN_FLOOR + (1 - MARGIN_FLOOR) * margin)),
|
|
1073
1090
|
rationale: `${best.groupBars}-bar groups from bar ${best.phase}: harmonic change ${Math.round(best.contrast * 100)}% stronger on group heads, ${cadencePart}`
|
|
1074
1091
|
};
|
|
1075
1092
|
}
|
|
1076
1093
|
|
|
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
1094
|
// src/analyze/form/phrase.ts
|
|
1621
1095
|
var CADENCE_BOUNDARY_STRENGTH = {
|
|
1622
1096
|
authentic: 0.9,
|
|
@@ -1668,7 +1142,7 @@ function readBoundary(boundary) {
|
|
|
1668
1142
|
(a, b) => b[1] - a[1] || SIGNAL_ORDER.indexOf(a[0]) - SIGNAL_ORDER.indexOf(b[0])
|
|
1669
1143
|
);
|
|
1670
1144
|
return {
|
|
1671
|
-
strength:
|
|
1145
|
+
strength: _chunkIFZDWIKMcjs.combineEvidence.call(void 0, signals.map(([, strength]) => strength)),
|
|
1672
1146
|
signals: signals.map(([signal]) => signal)
|
|
1673
1147
|
};
|
|
1674
1148
|
}
|
|
@@ -1712,29 +1186,18 @@ function choosePhrasePath(beats, strengths, minPhraseBeats, expected) {
|
|
|
1712
1186
|
return path;
|
|
1713
1187
|
}
|
|
1714
1188
|
function arrivalEnd(timeline, atBeat) {
|
|
1715
|
-
return _nullishCoalesce(_optionalChain([
|
|
1716
|
-
}
|
|
1717
|
-
function barSpanOf(startBeat, endBeat, meters) {
|
|
1718
|
-
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, startBeat, meters);
|
|
1719
|
-
const lastBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, endBeat, meters);
|
|
1720
|
-
const partOf = (beat, bar) => {
|
|
1721
|
-
const barStart = _chunkS5BKZZGMcjs.barPositionToBeat.call(void 0, { bar, beat: 0 }, meters);
|
|
1722
|
-
const barLength = _chunkS5BKZZGMcjs.beatsPerBarAt.call(void 0, barStart, meters);
|
|
1723
|
-
return barLength > 0 ? (beat - barStart) / barLength : 0;
|
|
1724
|
-
};
|
|
1725
|
-
const bars = lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);
|
|
1726
|
-
return Math.round(bars * 100) / 100;
|
|
1189
|
+
return _nullishCoalesce(_optionalChain([_chunkIFZDWIKMcjs.segmentStartingAt.call(void 0, timeline.segments, atBeat), 'optionalAccess', _43 => _43.endBeat]), () => ( atBeat));
|
|
1727
1190
|
}
|
|
1728
1191
|
function keyContextFor(opts, notes, meters, totalBeats) {
|
|
1729
1192
|
if (opts.key !== void 0) {
|
|
1730
1193
|
return opts.key;
|
|
1731
1194
|
}
|
|
1732
|
-
const regions =
|
|
1195
|
+
const regions = _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, notes, {
|
|
1733
1196
|
meters,
|
|
1734
1197
|
totalBeats,
|
|
1735
1198
|
budget: opts.budget
|
|
1736
1199
|
});
|
|
1737
|
-
const keyAt =
|
|
1200
|
+
const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, regions, _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, regions), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))));
|
|
1738
1201
|
return (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
|
|
1739
1202
|
}
|
|
1740
1203
|
function restBoundaries(boundaries, notes, meters, restBeats) {
|
|
@@ -1748,7 +1211,7 @@ function restBoundaries(boundaries, notes, meters, restBeats) {
|
|
|
1748
1211
|
boundaries,
|
|
1749
1212
|
note.startBeat,
|
|
1750
1213
|
"rest",
|
|
1751
|
-
REST_STRENGTH *
|
|
1214
|
+
REST_STRENGTH * _chunkIFZDWIKMcjs.clamp01.call(void 0, bar > 0 ? gap / bar : 1)
|
|
1752
1215
|
);
|
|
1753
1216
|
}
|
|
1754
1217
|
covered = Math.max(covered, note.startBeat + note.durationBeat);
|
|
@@ -1758,7 +1221,7 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
|
|
|
1758
1221
|
if (!(windowBeats > 0)) {
|
|
1759
1222
|
return;
|
|
1760
1223
|
}
|
|
1761
|
-
const slices =
|
|
1224
|
+
const slices = _chunkIFZDWIKMcjs.sliceBars.call(void 0, notes, meters, spanStart, spanEnd, budget);
|
|
1762
1225
|
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, slices.length * notes.length, "form repetition comparisons", budget);
|
|
1763
1226
|
const onsetsIn = (from, to) => notes.filter((note) => note.startBeat >= from - _chunkS5BKZZGMcjs.BEAT_EPS && note.startBeat < to - _chunkS5BKZZGMcjs.BEAT_EPS);
|
|
1764
1227
|
let comparisons = 0;
|
|
@@ -1774,7 +1237,7 @@ function repetitionBoundaries(boundaries, notes, meters, spanStart, spanEnd, win
|
|
|
1774
1237
|
}
|
|
1775
1238
|
comparisons += before.length * after.length;
|
|
1776
1239
|
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, comparisons, "form repetition melody comparisons", budget);
|
|
1777
|
-
const similarity = melodicSimilarity(before, after);
|
|
1240
|
+
const similarity = _chunkIFZDWIKMcjs.melodicSimilarity.call(void 0, before, after);
|
|
1778
1241
|
if (similarity >= REPETITION_THRESHOLD) {
|
|
1779
1242
|
addSignal(boundaries, beat, "repetition", REPETITION_STRENGTH * similarity);
|
|
1780
1243
|
}
|
|
@@ -1803,7 +1266,7 @@ function structuralWeightOf(cadence, confidence, aligned, isLast) {
|
|
|
1803
1266
|
if (aligned) {
|
|
1804
1267
|
weight += HYPERMETRIC_CLOSE_BONUS;
|
|
1805
1268
|
}
|
|
1806
|
-
return
|
|
1269
|
+
return _chunkIFZDWIKMcjs.clamp01.call(void 0, weight);
|
|
1807
1270
|
}
|
|
1808
1271
|
function describeCadence(cadence) {
|
|
1809
1272
|
if (cadence === null || cadence.cadence.type === null) {
|
|
@@ -1814,7 +1277,7 @@ function describeCadence(cadence) {
|
|
|
1814
1277
|
return `a ${graded} cadence at beat ${cadence.atBeat}`;
|
|
1815
1278
|
}
|
|
1816
1279
|
function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
1817
|
-
|
|
1280
|
+
_chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, timeline);
|
|
1818
1281
|
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "phrase meters");
|
|
1819
1282
|
_chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "phrase notes", {
|
|
1820
1283
|
allowNonPositiveDuration: true,
|
|
@@ -1830,12 +1293,12 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1830
1293
|
const lastSegment = segments[segments.length - 1];
|
|
1831
1294
|
const firstOnset = sounding.reduce(
|
|
1832
1295
|
(first, n) => Math.min(first, n.startBeat),
|
|
1833
|
-
_nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess',
|
|
1296
|
+
_nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _44 => _44.startBeat]), () => ( 0))
|
|
1834
1297
|
);
|
|
1835
|
-
const spanStart =
|
|
1298
|
+
const spanStart = _chunkIFZDWIKMcjs.barGridStart.call(void 0, firstOnset, meters);
|
|
1836
1299
|
const notesEnd = sounding.reduce(
|
|
1837
1300
|
(end, n) => Math.max(end, n.startBeat + n.durationBeat),
|
|
1838
|
-
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess',
|
|
1301
|
+
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _45 => _45.endBeat]), () => ( 0))
|
|
1839
1302
|
);
|
|
1840
1303
|
const spanEnd = Math.max(spanStart, _nullishCoalesce(opts.totalBeats, () => ( notesEnd)));
|
|
1841
1304
|
_chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "phrase totalBeats");
|
|
@@ -1850,7 +1313,7 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1850
1313
|
const minPhraseBeats = _nullishCoalesce(opts.minPhraseBeats, () => ( barBeats));
|
|
1851
1314
|
_chunkS5BKZZGMcjs.assertRange.call(void 0, minPhraseBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "phrase minPhraseBeats");
|
|
1852
1315
|
const key = keyContextFor(opts, sounding, meters, spanEnd);
|
|
1853
|
-
const cadences =
|
|
1316
|
+
const cadences = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, key);
|
|
1854
1317
|
const grouping = _nullishCoalesce(opts.hypermeter, () => ( hypermeter(sounding, meters, {
|
|
1855
1318
|
totalBeats: spanEnd,
|
|
1856
1319
|
cadenceBeats: cadences.map((hit) => hit.atBeat),
|
|
@@ -1916,16 +1379,16 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1916
1379
|
let from = 0;
|
|
1917
1380
|
for (let n = 0; n < path.length; n += 1) {
|
|
1918
1381
|
const to = _nullishCoalesce(path[n], () => ( stops.length - 1));
|
|
1919
|
-
const startBeat = _nullishCoalesce(_optionalChain([stops, 'access',
|
|
1382
|
+
const startBeat = _nullishCoalesce(_optionalChain([stops, 'access', _46 => _46[from], 'optionalAccess', _47 => _47.beat]), () => ( spanStart));
|
|
1920
1383
|
const stop = stops[to];
|
|
1921
|
-
const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess',
|
|
1384
|
+
const endBeat = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _48 => _48.beat]), () => ( spanEnd));
|
|
1922
1385
|
const reading = _nullishCoalesce(readings[to], () => ( { strength: 0, signals: [] }));
|
|
1923
1386
|
const length = endBeat - startBeat;
|
|
1924
|
-
const confidence =
|
|
1387
|
+
const confidence = _chunkIFZDWIKMcjs.clamp01.call(void 0,
|
|
1925
1388
|
CLOSING_SHARE * reading.strength + (1 - CLOSING_SHARE) * Math.max(0, lengthFit(length, expected))
|
|
1926
1389
|
);
|
|
1927
1390
|
const isLast = n === path.length - 1;
|
|
1928
|
-
const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess',
|
|
1391
|
+
const closing = _nullishCoalesce(_optionalChain([stop, 'optionalAccess', _49 => _49.cadence]), () => ( null));
|
|
1929
1392
|
const cadence = closing !== null && closing.atBeat >= startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && closing.atBeat < endBeat - _chunkS5BKZZGMcjs.BEAT_EPS ? closing : null;
|
|
1930
1393
|
const structuralWeight = structuralWeightOf(
|
|
1931
1394
|
cadence,
|
|
@@ -1934,7 +1397,7 @@ function phrasesFromTimeline(timeline, notes, opts = {}) {
|
|
|
1934
1397
|
isLast
|
|
1935
1398
|
);
|
|
1936
1399
|
const signals = cadence === null ? reading.signals.filter((signal) => signal !== "cadence") : reading.signals;
|
|
1937
|
-
const bars = barSpanOf(startBeat, endBeat, meters);
|
|
1400
|
+
const bars = Math.round(_chunkIFZDWIKMcjs.barSpanOf.call(void 0, startBeat, endBeat, meters) * 100) / 100;
|
|
1938
1401
|
phrases.push({
|
|
1939
1402
|
startBeat,
|
|
1940
1403
|
endBeat,
|
|
@@ -1986,12 +1449,12 @@ function isSilent(unit) {
|
|
|
1986
1449
|
return unit.notes.length === 0;
|
|
1987
1450
|
}
|
|
1988
1451
|
function unitSimilarity(a, b) {
|
|
1989
|
-
const harmony =
|
|
1452
|
+
const harmony = _chunkIFZDWIKMcjs.weightSimilarity.call(void 0, a.weights, b.weights);
|
|
1990
1453
|
if (a.notes.length < 2 || b.notes.length < 2) {
|
|
1991
1454
|
return a.notes.length === b.notes.length ? harmony : 0;
|
|
1992
1455
|
}
|
|
1993
|
-
const melody = melodicSimilarity(a.notes, b.notes);
|
|
1994
|
-
return
|
|
1456
|
+
const melody = _chunkIFZDWIKMcjs.melodicSimilarity.call(void 0, a.notes, b.notes);
|
|
1457
|
+
return _chunkIFZDWIKMcjs.clamp01.call(void 0, MELODY_SHARE * melody + HARMONY_SHARE * harmony);
|
|
1995
1458
|
}
|
|
1996
1459
|
function sectionsFromNotes(notes, opts = {}) {
|
|
1997
1460
|
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, opts, "section meters");
|
|
@@ -2003,7 +1466,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2003
1466
|
if (sounding.length === 0) {
|
|
2004
1467
|
return [];
|
|
2005
1468
|
}
|
|
2006
|
-
const firstOnset =
|
|
1469
|
+
const firstOnset = _chunkIFZDWIKMcjs.barGridStart.call(void 0, _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding), meters);
|
|
2007
1470
|
const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
2008
1471
|
const spanEnd = Math.max(firstOnset, _nullishCoalesce(opts.totalBeats, () => ( lastEnd)));
|
|
2009
1472
|
_chunkS5BKZZGMcjs.assertRange.call(void 0, spanEnd, 0, Number.MAX_SAFE_INTEGER, "section totalBeats");
|
|
@@ -2012,7 +1475,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2012
1475
|
const unitBars = _nullishCoalesce(opts.unitBars, () => ( hypermeter(sounding, meters, { totalBeats: spanEnd, budget: opts.budget }).groupBars));
|
|
2013
1476
|
_chunkS5BKZZGMcjs.assertPositiveInt.call(void 0, unitBars, "section unitBars");
|
|
2014
1477
|
const firstBar = _chunkS5BKZZGMcjs.barIndexAt.call(void 0, firstOnset, meters);
|
|
2015
|
-
const lastBar =
|
|
1478
|
+
const lastBar = _chunkIFZDWIKMcjs.lastBarOf.call(void 0, meters, firstOnset, spanEnd);
|
|
2016
1479
|
const startBar = Math.max(0, firstBar);
|
|
2017
1480
|
const unitCount = Math.max(1, Math.ceil((lastBar - startBar + 1) / unitBars));
|
|
2018
1481
|
_chunkS5BKZZGMcjs.assertGenerationBudget.call(void 0, unitCount * unitCount, "form section comparisons", opts.budget);
|
|
@@ -2029,7 +1492,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2029
1492
|
startBeat,
|
|
2030
1493
|
endBeat,
|
|
2031
1494
|
notes: unitNotes,
|
|
2032
|
-
weights:
|
|
1495
|
+
weights: _chunkIFZDWIKMcjs.windowWeights.call(void 0, unitNotes, startBeat, endBeat, meters).weights
|
|
2033
1496
|
});
|
|
2034
1497
|
}
|
|
2035
1498
|
let comparisons = 0;
|
|
@@ -2093,8 +1556,8 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2093
1556
|
while (end < units.length && labels[end] === null) {
|
|
2094
1557
|
end += 1;
|
|
2095
1558
|
}
|
|
2096
|
-
const startBeat = _nullishCoalesce(_optionalChain([units, 'access',
|
|
2097
|
-
const endBeat = _nullishCoalesce(_optionalChain([units, 'access',
|
|
1559
|
+
const startBeat = _nullishCoalesce(_optionalChain([units, 'access', _50 => _50[index], 'optionalAccess', _51 => _51.startBeat]), () => ( 0));
|
|
1560
|
+
const endBeat = _nullishCoalesce(_optionalChain([units, 'access', _52 => _52[end - 1], 'optionalAccess', _53 => _53.endBeat]), () => ( spanEnd));
|
|
2098
1561
|
const sectionFirstBar = startBar + index * unitBars;
|
|
2099
1562
|
const sectionLastBar = Math.max(
|
|
2100
1563
|
sectionFirstBar,
|
|
@@ -2105,7 +1568,7 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2105
1568
|
if (first === void 0) {
|
|
2106
1569
|
firstByLabel.set(label, sectionIndex);
|
|
2107
1570
|
}
|
|
2108
|
-
const similarity = first === void 0 ? 1 :
|
|
1571
|
+
const similarity = first === void 0 ? 1 : _chunkIFZDWIKMcjs.clamp01.call(void 0, scoreSum / Math.max(1, sounded));
|
|
2109
1572
|
sections.push({
|
|
2110
1573
|
label,
|
|
2111
1574
|
startBeat,
|
|
@@ -2113,163 +1576,284 @@ function sectionsFromNotes(notes, opts = {}) {
|
|
|
2113
1576
|
bars: sectionLastBar - sectionFirstBar + 1,
|
|
2114
1577
|
firstOccurrence: _nullishCoalesce(first, () => ( sectionIndex)),
|
|
2115
1578
|
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',
|
|
1579
|
+
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', _54 => _54[first], 'optionalAccess', _55 => _55.startBeat]), () => ( 0))}`
|
|
2117
1580
|
});
|
|
2118
1581
|
index = end;
|
|
2119
1582
|
}
|
|
2120
1583
|
return sections;
|
|
2121
1584
|
}
|
|
2122
1585
|
|
|
2123
|
-
// src/analyze/
|
|
2124
|
-
var
|
|
2125
|
-
function
|
|
2126
|
-
|
|
1586
|
+
// src/analyze/reference/compare.ts
|
|
1587
|
+
var RATIONALE_FIELDS = 2;
|
|
1588
|
+
function rationaleOf(comparison) {
|
|
1589
|
+
const measured = [];
|
|
1590
|
+
for (const group of [comparison.form, comparison.harmony, comparison.melody, comparison.rhythm]) {
|
|
1591
|
+
for (const [name, value] of Object.entries(group)) {
|
|
1592
|
+
if (value !== null) {
|
|
1593
|
+
measured.push({ name, value, order: measured.length });
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (measured.length === 0) {
|
|
1598
|
+
return "Nothing to compare: neither profile has material for any field";
|
|
1599
|
+
}
|
|
1600
|
+
const describe = (entries) => entries.map((entry) => `${entry.name} ${Math.round(entry.value * 100)}%`).join(", ");
|
|
1601
|
+
const highest = [...measured].sort((x, y) => y.value - x.value || x.order - y.order);
|
|
1602
|
+
const lowest = [...measured].sort((x, y) => x.value - y.value || x.order - y.order);
|
|
1603
|
+
return `Most alike: ${describe(highest.slice(0, RATIONALE_FIELDS))}; least alike: ${describe(lowest.slice(0, RATIONALE_FIELDS))}`;
|
|
1604
|
+
}
|
|
1605
|
+
function compareReferences(a, b, opts) {
|
|
1606
|
+
_chunkIFZDWIKMcjs.assertReferenceProfile.call(void 0, a, "reference profile a");
|
|
1607
|
+
_chunkIFZDWIKMcjs.assertReferenceProfile.call(void 0, b, "reference profile b");
|
|
1608
|
+
const { budget } = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "compare options");
|
|
1609
|
+
const structural = (profile) => profile.harmony.chords.filter((chord) => chord.level === "structural");
|
|
1610
|
+
const cadences = (profile) => profile.form.phrases.map((phrase) => _nullishCoalesce(_optionalChain([phrase, 'access', _56 => _56.cadence, 'optionalAccess', _57 => _57.type]), () => ( null)));
|
|
1611
|
+
const phraseMelodies = (profile) => profile.form.phrases.map((phrase) => phrase.melody);
|
|
1612
|
+
const comparison = {
|
|
1613
|
+
form: {
|
|
1614
|
+
sectionSequenceSimilarity: _chunkIFZDWIKMcjs.sectionSequenceSimilarity.call(void 0,
|
|
1615
|
+
a.form.sections.map((section) => section.label),
|
|
1616
|
+
b.form.sections.map((section) => section.label)
|
|
1617
|
+
),
|
|
1618
|
+
phraseLengthSimilarity: _chunkIFZDWIKMcjs.phraseLengthSimilarity.call(void 0,
|
|
1619
|
+
a.form.phrases.map((phrase) => phrase.bars),
|
|
1620
|
+
b.form.phrases.map((phrase) => phrase.bars)
|
|
1621
|
+
)
|
|
1622
|
+
},
|
|
1623
|
+
harmony: {
|
|
1624
|
+
progressionSimilarity: _chunkIFZDWIKMcjs.progressionSimilarity.call(void 0, structural(a), structural(b), budget),
|
|
1625
|
+
functionSimilarity: _chunkIFZDWIKMcjs.functionSimilarity.call(void 0, a.harmony.chords, b.harmony.chords),
|
|
1626
|
+
cadenceSimilarity: _chunkIFZDWIKMcjs.cadenceSimilarity.call(void 0, cadences(a), cadences(b)),
|
|
1627
|
+
keyPlanSimilarity: _chunkIFZDWIKMcjs.keyPlanSimilarity.call(void 0,
|
|
1628
|
+
a.harmony.keys.map((region) => region.key),
|
|
1629
|
+
b.harmony.keys.map((region) => region.key)
|
|
1630
|
+
),
|
|
1631
|
+
harmonicRhythmOnsetSimilarity: _chunkIFZDWIKMcjs.onsetSimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm),
|
|
1632
|
+
harmonicRhythmDensitySimilarity: _chunkIFZDWIKMcjs.densitySimilarity.call(void 0, a.harmony.rhythm, b.harmony.rhythm)
|
|
1633
|
+
},
|
|
1634
|
+
melody: {
|
|
1635
|
+
surfaceSimilarity: _chunkIFZDWIKMcjs.surfaceSimilarity.call(void 0, a.melody.motifs, b.melody.motifs),
|
|
1636
|
+
motifStructureSimilarity: _chunkIFZDWIKMcjs.motifStructureSimilarity.call(void 0,
|
|
1637
|
+
{ graph: a.melody.graph, spanBeats: a.span.endBeat - a.span.startBeat },
|
|
1638
|
+
{ graph: b.melody.graph, spanBeats: b.span.endBeat - b.span.startBeat }
|
|
1639
|
+
),
|
|
1640
|
+
contourSimilarity: _chunkIFZDWIKMcjs.contourSimilarity.call(void 0, phraseMelodies(a), phraseMelodies(b)),
|
|
1641
|
+
registerSimilarity: _chunkIFZDWIKMcjs.registerSimilarity.call(void 0,
|
|
1642
|
+
{ phrases: phraseMelodies(a), mean: _nullishCoalesce(_optionalChain([a, 'access', _58 => _58.melody, 'access', _59 => _59.register, 'optionalAccess', _60 => _60.mean]), () => ( null)) },
|
|
1643
|
+
{ phrases: phraseMelodies(b), mean: _nullishCoalesce(_optionalChain([b, 'access', _61 => _61.melody, 'access', _62 => _62.register, 'optionalAccess', _63 => _63.mean]), () => ( null)) }
|
|
1644
|
+
)
|
|
1645
|
+
},
|
|
1646
|
+
rhythm: {
|
|
1647
|
+
onsetSimilarity: _chunkIFZDWIKMcjs.onsetSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1648
|
+
densitySimilarity: _chunkIFZDWIKMcjs.densitySimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1649
|
+
durationSimilarity: _chunkIFZDWIKMcjs.durationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm),
|
|
1650
|
+
syncopationSimilarity: _chunkIFZDWIKMcjs.syncopationSimilarity.call(void 0, a.melody.rhythm, b.melody.rhythm)
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
return { ...comparison, rationale: rationaleOf(comparison) };
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// src/analyze/reference/profile.ts
|
|
1657
|
+
function spanOf(sounding, timeline, meters, totalBeats) {
|
|
1658
|
+
const segments = timeline.segments;
|
|
1659
|
+
const firstSegment = segments[0];
|
|
1660
|
+
const lastSegment = segments[segments.length - 1];
|
|
1661
|
+
const firstOnset = _chunkIFZDWIKMcjs.firstSoundingBeat.call(void 0, sounding, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _64 => _64.startBeat]), () => ( 0)));
|
|
1662
|
+
const startBeat = _chunkIFZDWIKMcjs.barGridStart.call(void 0,
|
|
1663
|
+
Math.min(firstOnset, _nullishCoalesce(_optionalChain([firstSegment, 'optionalAccess', _65 => _65.startBeat]), () => ( firstOnset))),
|
|
1664
|
+
meters
|
|
1665
|
+
);
|
|
1666
|
+
const notesEnd = sounding.reduce(
|
|
1667
|
+
(end, note) => Math.max(end, note.startBeat + note.durationBeat),
|
|
1668
|
+
_nullishCoalesce(_optionalChain([lastSegment, 'optionalAccess', _66 => _66.endBeat]), () => ( 0))
|
|
1669
|
+
);
|
|
1670
|
+
return { startBeat, endBeat: Math.max(startBeat, _nullishCoalesce(totalBeats, () => ( notesEnd))) };
|
|
2127
1671
|
}
|
|
2128
|
-
function
|
|
2129
|
-
|
|
2130
|
-
return size === 1 || size === 2;
|
|
1672
|
+
function sameKey(a, b) {
|
|
1673
|
+
return _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, a.scale.rootPc) === _chunkS5BKZZGMcjs.pitchClassOf.call(void 0, b.scale.rootPc) && a.scale.modeMask12 === b.scale.modeMask12;
|
|
2131
1674
|
}
|
|
2132
|
-
function
|
|
2133
|
-
|
|
1675
|
+
function keyIndexAt(regions, beat, prevailing) {
|
|
1676
|
+
const holding = regions.findIndex((region) => region.startBeat <= beat && beat < region.endBeat);
|
|
1677
|
+
if (holding >= 0) {
|
|
1678
|
+
return holding;
|
|
1679
|
+
}
|
|
1680
|
+
return Math.max(
|
|
1681
|
+
0,
|
|
1682
|
+
regions.findIndex((region) => sameKey(region.key, prevailing))
|
|
1683
|
+
);
|
|
2134
1684
|
}
|
|
2135
|
-
function
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
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";
|
|
1685
|
+
function sectionAt(sections, beat) {
|
|
1686
|
+
const index = sections.findIndex(
|
|
1687
|
+
(section) => beat >= section.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && beat < section.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS
|
|
1688
|
+
);
|
|
1689
|
+
return index < 0 ? null : index;
|
|
1690
|
+
}
|
|
1691
|
+
function readingsFor(notes, opts = {}) {
|
|
1692
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "reference options");
|
|
1693
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, asked, "reference meters");
|
|
1694
|
+
const budget = asked.budget;
|
|
1695
|
+
if (asked.timeline === void 0) {
|
|
1696
|
+
return _chunkIFZDWIKMcjs.chordTimelineFromNotes.call(void 0, notes, {
|
|
1697
|
+
meters,
|
|
1698
|
+
key: asked.key,
|
|
1699
|
+
totalBeats: asked.totalBeats,
|
|
1700
|
+
budget
|
|
1701
|
+
});
|
|
2235
1702
|
}
|
|
2236
|
-
|
|
1703
|
+
const timeline = _chunkIFZDWIKMcjs.assertChordTimeline.call(void 0, asked.timeline, "reference timeline");
|
|
1704
|
+
let keys;
|
|
1705
|
+
if (asked.key === void 0) {
|
|
1706
|
+
keys = _chunkIFZDWIKMcjs.keyTimelineFromNotes.call(void 0, notes, { meters, totalBeats: asked.totalBeats, budget });
|
|
1707
|
+
} else {
|
|
1708
|
+
const sounding = _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
|
|
1709
|
+
allowNonPositiveDuration: true,
|
|
1710
|
+
budget
|
|
1711
|
+
}).filter((note) => note.durationBeat > 0);
|
|
1712
|
+
const span = spanOf(sounding, timeline, meters, asked.totalBeats);
|
|
1713
|
+
keys = [
|
|
1714
|
+
{
|
|
1715
|
+
startBeat: span.startBeat,
|
|
1716
|
+
endBeat: span.endBeat,
|
|
1717
|
+
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, asked.key),
|
|
1718
|
+
confidence: 1
|
|
1719
|
+
}
|
|
1720
|
+
];
|
|
1721
|
+
}
|
|
1722
|
+
return { timeline, keys, prevailingKey: _nullishCoalesce(_chunkIFZDWIKMcjs.prevailingKeyOf.call(void 0, keys), () => ( _chunk3R3QVKY5cjs.resolveKey.call(void 0, _chunk3R3QVKY5cjs.majorKey.call(void 0, 0)))) };
|
|
2237
1723
|
}
|
|
2238
|
-
function
|
|
2239
|
-
|
|
2240
|
-
const
|
|
2241
|
-
const
|
|
2242
|
-
const
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
1724
|
+
function referenceFromReadings(notes, readings, opts = {}) {
|
|
1725
|
+
const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, opts, "reference options");
|
|
1726
|
+
const meters = _chunkS5BKZZGMcjs.resolveMeters.call(void 0, asked, "reference meters");
|
|
1727
|
+
const budget = asked.budget;
|
|
1728
|
+
const sounding = _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, notes, "reference notes", {
|
|
1729
|
+
allowNonPositiveDuration: true,
|
|
1730
|
+
budget
|
|
1731
|
+
}).filter((note) => note.durationBeat > 0);
|
|
1732
|
+
if (asked.totalBeats !== void 0) {
|
|
1733
|
+
_chunkS5BKZZGMcjs.assertRange.call(void 0, asked.totalBeats, 0, Number.MAX_SAFE_INTEGER, "reference totalBeats");
|
|
1734
|
+
}
|
|
1735
|
+
const line = _chunkIFZDWIKMcjs.orderedNotes.call(void 0, _nullishCoalesce(asked.melody, () => ( notes)), "reference melody", budget);
|
|
1736
|
+
const { timeline, prevailingKey } = readings;
|
|
1737
|
+
const span = spanOf(sounding, timeline, meters, asked.totalBeats);
|
|
1738
|
+
const spanEnd = span.endBeat;
|
|
1739
|
+
const keyAt = _chunkIFZDWIKMcjs.keyLookup.call(void 0, readings.keys, prevailingKey);
|
|
1740
|
+
const scaleAt = (beat) => _chunk3R3QVKY5cjs.scaleOf.call(void 0, keyAt(beat));
|
|
1741
|
+
const cadenceBeats = _chunkIFZDWIKMcjs.detectCadences.call(void 0, timeline, scaleAt).map((hit) => hit.atBeat);
|
|
1742
|
+
const grouping = hypermeter(sounding, meters, { cadenceBeats, totalBeats: spanEnd, budget });
|
|
1743
|
+
const phrases = phrasesFromTimeline(timeline, notes, {
|
|
1744
|
+
meters,
|
|
1745
|
+
key: scaleAt,
|
|
1746
|
+
hypermeter: grouping,
|
|
1747
|
+
totalBeats: spanEnd,
|
|
1748
|
+
budget
|
|
1749
|
+
});
|
|
1750
|
+
const cadences = /* @__PURE__ */ new Map();
|
|
1751
|
+
for (const closing of structuralCadences(phrases)) {
|
|
1752
|
+
const type = closing.cadence.type;
|
|
1753
|
+
if (type !== null) {
|
|
1754
|
+
cadences.set(closing.phraseIndex, {
|
|
1755
|
+
atBeat: closing.atBeat,
|
|
1756
|
+
type,
|
|
1757
|
+
weight: closing.weight
|
|
1758
|
+
});
|
|
2251
1759
|
}
|
|
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
1760
|
}
|
|
2272
|
-
|
|
1761
|
+
const sections = sectionsFromNotes(notes, {
|
|
1762
|
+
meters,
|
|
1763
|
+
unitBars: grouping.groupBars,
|
|
1764
|
+
totalBeats: spanEnd,
|
|
1765
|
+
budget
|
|
1766
|
+
});
|
|
1767
|
+
const keys = readings.keys.length === 0 && timeline.segments.length > 0 ? [
|
|
1768
|
+
{
|
|
1769
|
+
startBeat: span.startBeat,
|
|
1770
|
+
endBeat: spanEnd,
|
|
1771
|
+
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, prevailingKey),
|
|
1772
|
+
confidence: 0
|
|
1773
|
+
}
|
|
1774
|
+
] : readings.keys.map((region) => ({
|
|
1775
|
+
startBeat: region.startBeat,
|
|
1776
|
+
endBeat: region.endBeat,
|
|
1777
|
+
key: _chunk3R3QVKY5cjs.resolveKey.call(void 0, region.key),
|
|
1778
|
+
confidence: region.confidence
|
|
1779
|
+
}));
|
|
1780
|
+
const chords = _chunkIFZDWIKMcjs.reduceProgression.call(void 0, timeline, scaleAt).map((reduced) => {
|
|
1781
|
+
const index = keyIndexAt(keys, (reduced.startBeat + reduced.endBeat) / 2, prevailingKey);
|
|
1782
|
+
const key = keys[index].key;
|
|
1783
|
+
return {
|
|
1784
|
+
startBeat: reduced.startBeat,
|
|
1785
|
+
endBeat: reduced.endBeat,
|
|
1786
|
+
key: index,
|
|
1787
|
+
roman: _chunkIFZDWIKMcjs.chordToRoman.call(void 0, reduced.chord, key),
|
|
1788
|
+
function: _chunkIFZDWIKMcjs.functionOf.call(void 0, reduced.chord, key),
|
|
1789
|
+
level: reduced.level
|
|
1790
|
+
};
|
|
1791
|
+
});
|
|
1792
|
+
const chordOnsets = timeline.segments.map((segment) => ({
|
|
1793
|
+
pitch: 60,
|
|
1794
|
+
startBeat: segment.startBeat,
|
|
1795
|
+
durationBeat: segment.endBeat - segment.startBeat
|
|
1796
|
+
}));
|
|
1797
|
+
const harmonyRhythm = _chunkIFZDWIKMcjs.analyzeRhythm.call(void 0, chordOnsets, { meters, totalBeats: spanEnd, budget });
|
|
1798
|
+
const motifs = _chunkIFZDWIKMcjs.extractMotifs.call(void 0, line, { budget });
|
|
1799
|
+
const graph = _chunkIFZDWIKMcjs.motifGraph.call(void 0, line, motifs, { key: prevailingKey, budget });
|
|
1800
|
+
let register = null;
|
|
1801
|
+
if (line.length > 0) {
|
|
1802
|
+
const pitches = line.map((note) => note.pitch);
|
|
1803
|
+
register = {
|
|
1804
|
+
low: Math.min(...pitches),
|
|
1805
|
+
high: Math.max(...pitches),
|
|
1806
|
+
mean: _chunkIFZDWIKMcjs.weightedMean.call(void 0, line)
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
const lineMean = _nullishCoalesce(_optionalChain([register, 'optionalAccess', _67 => _67.mean]), () => ( 0));
|
|
1810
|
+
const referenceMotifs = motifs.map((motif) => {
|
|
1811
|
+
const first = motif.occurrences[0];
|
|
1812
|
+
return {
|
|
1813
|
+
intervals: motif.intervals,
|
|
1814
|
+
rhythm: motif.rhythm,
|
|
1815
|
+
unitBeats: motif.notes.length < 2 ? 0 : motif.notes[1].startBeat - motif.notes[0].startBeat,
|
|
1816
|
+
spanBeats: first === void 0 ? 0 : first.endBeat - first.startBeat,
|
|
1817
|
+
occurrences: motif.occurrences.length
|
|
1818
|
+
};
|
|
1819
|
+
});
|
|
1820
|
+
const referencePhrases = phrases.map((phrase, index) => ({
|
|
1821
|
+
startBeat: phrase.startBeat,
|
|
1822
|
+
endBeat: phrase.endBeat,
|
|
1823
|
+
bars: _chunkIFZDWIKMcjs.barSpanOf.call(void 0, phrase.startBeat, phrase.endBeat, meters),
|
|
1824
|
+
section: sectionAt(sections, phrase.startBeat),
|
|
1825
|
+
confidence: phrase.confidence,
|
|
1826
|
+
cadence: _nullishCoalesce(cadences.get(index), () => ( null)),
|
|
1827
|
+
melody: _chunkIFZDWIKMcjs.phraseMelody.call(void 0, line, phrase.startBeat, phrase.endBeat, lineMean, meters, budget),
|
|
1828
|
+
motifNodes: graph.nodes.flatMap(
|
|
1829
|
+
(node, nodeIndex) => node.startBeat >= phrase.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS && node.startBeat < phrase.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS ? [nodeIndex] : []
|
|
1830
|
+
)
|
|
1831
|
+
}));
|
|
1832
|
+
return {
|
|
1833
|
+
profileVersion: _chunkIFZDWIKMcjs.REFERENCE_PROFILE_VERSION,
|
|
1834
|
+
span: {
|
|
1835
|
+
startBeat: span.startBeat,
|
|
1836
|
+
endBeat: spanEnd,
|
|
1837
|
+
bars: _chunkIFZDWIKMcjs.barSpanOf.call(void 0, span.startBeat, spanEnd, meters)
|
|
1838
|
+
},
|
|
1839
|
+
meters,
|
|
1840
|
+
form: {
|
|
1841
|
+
hypermeter: { groupBars: grouping.groupBars, confidence: grouping.confidence },
|
|
1842
|
+
sections,
|
|
1843
|
+
phrases: referencePhrases
|
|
1844
|
+
},
|
|
1845
|
+
harmony: { keys, chords, rhythm: harmonyRhythm },
|
|
1846
|
+
melody: {
|
|
1847
|
+
register,
|
|
1848
|
+
shape: line.length < 2 ? null : _chunkIFZDWIKMcjs.melodicContour.call(void 0, line).shape,
|
|
1849
|
+
motifs: referenceMotifs,
|
|
1850
|
+
graph,
|
|
1851
|
+
rhythm: _chunkIFZDWIKMcjs.analyzeRhythm.call(void 0, line, { meters, totalBeats: spanEnd, budget })
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
function analyzeReference(notes, opts) {
|
|
1856
|
+
return referenceFromReadings(notes, readingsFor(notes, opts), opts);
|
|
2273
1857
|
}
|
|
2274
1858
|
|
|
2275
1859
|
|
|
@@ -2286,9 +1870,5 @@ function reduceProgression(timeline, key, opts = {}) {
|
|
|
2286
1870
|
|
|
2287
1871
|
|
|
2288
1872
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
exports.trackRoleOf = trackRoleOf; exports.analyzePolyphony = analyzePolyphony; exports.analyzeArrangement = analyzeArrangement; exports.createArrangementSession = createArrangementSession; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.hypermeter = hypermeter; exports.melodicContour = melodicContour; exports.extractMotifs = extractMotifs; exports.motifFromNotes = motifFromNotes; exports.relateMotifs = relateMotifs; exports.compareMelodies = compareMelodies; exports.melodicSimilarity = melodicSimilarity; exports.phrasesFromTimeline = phrasesFromTimeline; exports.structuralCadences = structuralCadences; exports.sectionsFromNotes = sectionsFromNotes; exports.reduceProgression = reduceProgression;
|
|
2294
|
-
//# sourceMappingURL=chunk-HPVSH7SF.cjs.map
|
|
1873
|
+
exports.trackRoleOf = trackRoleOf; exports.analyzePolyphony = analyzePolyphony; exports.analyzeArrangement = analyzeArrangement; exports.createArrangementSession = createArrangementSession; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.hypermeter = hypermeter; exports.phrasesFromTimeline = phrasesFromTimeline; exports.structuralCadences = structuralCadences; exports.sectionsFromNotes = sectionsFromNotes; exports.compareReferences = compareReferences; exports.referenceFromReadings = referenceFromReadings; exports.analyzeReference = analyzeReference;
|
|
1874
|
+
//# sourceMappingURL=chunk-YFA7X2F6.cjs.map
|