@libraz/libcantus 0.9.5 → 1.0.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 +61 -270
- package/README_ja.md +61 -214
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +200 -353
- package/dist/analyze/index.d.ts +200 -353
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/{chunk-2PTFGVD7.js → chunk-ELCTZUJJ.js} +250 -132
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +93 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +87 -346
- package/dist/core/index.d.ts +87 -346
- package/dist/core/index.js +94 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +436 -1094
- package/dist/generate/index.d.ts +436 -1094
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-Cfx8xgH0.d.ts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-x2vcsduE.d.cts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +348 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +397 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4659 -249
- package/dist/model/index.d.ts +4659 -249
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +511 -171
- package/dist/theory/index.d.ts +511 -171
- package/dist/theory/index.js +118 -24
- package/package.json +6 -4
- package/dist/chunk-2A2QMA74.cjs +0 -261
- package/dist/chunk-2A2QMA74.cjs.map +0 -1
- package/dist/chunk-2PTFGVD7.js.map +0 -1
- package/dist/chunk-357ZDPVU.cjs +0 -162
- package/dist/chunk-357ZDPVU.cjs.map +0 -1
- package/dist/chunk-3KX76RLG.js +0 -1548
- package/dist/chunk-3KX76RLG.js.map +0 -1
- package/dist/chunk-4VZ5MZAM.cjs +0 -889
- package/dist/chunk-4VZ5MZAM.cjs.map +0 -1
- package/dist/chunk-7EONYVI5.js +0 -1204
- package/dist/chunk-7EONYVI5.js.map +0 -1
- package/dist/chunk-7JEP3MFY.js +0 -162
- package/dist/chunk-7JEP3MFY.js.map +0 -1
- package/dist/chunk-AID75RN3.js +0 -3176
- package/dist/chunk-AID75RN3.js.map +0 -1
- package/dist/chunk-BSLTLGM3.cjs +0 -497
- package/dist/chunk-BSLTLGM3.cjs.map +0 -1
- package/dist/chunk-CU6VG2YM.cjs +0 -109
- package/dist/chunk-CU6VG2YM.cjs.map +0 -1
- package/dist/chunk-DZMHIGI4.js +0 -337
- package/dist/chunk-DZMHIGI4.js.map +0 -1
- package/dist/chunk-EDOTEABK.js +0 -109
- package/dist/chunk-EDOTEABK.js.map +0 -1
- package/dist/chunk-HN2OEUN2.js +0 -33
- package/dist/chunk-HN2OEUN2.js.map +0 -1
- package/dist/chunk-KBTTEX7Q.js +0 -497
- package/dist/chunk-KBTTEX7Q.js.map +0 -1
- package/dist/chunk-KQN2ZYND.cjs +0 -724
- package/dist/chunk-KQN2ZYND.cjs.map +0 -1
- package/dist/chunk-MPTVJIVG.cjs +0 -3176
- package/dist/chunk-MPTVJIVG.cjs.map +0 -1
- package/dist/chunk-MTMZTMWM.cjs +0 -53
- package/dist/chunk-MTMZTMWM.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NNB67XA2.js +0 -724
- package/dist/chunk-NNB67XA2.js.map +0 -1
- package/dist/chunk-OL3IHHSJ.cjs +0 -1204
- package/dist/chunk-OL3IHHSJ.cjs.map +0 -1
- package/dist/chunk-PI3UXRWK.cjs +0 -528
- package/dist/chunk-PI3UXRWK.cjs.map +0 -1
- package/dist/chunk-R2VVFAIN.js +0 -528
- package/dist/chunk-R2VVFAIN.js.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-WFDXOLUB.cjs +0 -337
- package/dist/chunk-WFDXOLUB.cjs.map +0 -1
- package/dist/chunk-WTAN4ACG.cjs +0 -1548
- package/dist/chunk-WTAN4ACG.cjs.map +0 -1
- package/dist/chunk-YFJXS3AU.cjs +0 -33
- package/dist/chunk-YFJXS3AU.cjs.map +0 -1
- package/dist/chunk-YUE6TJ4M.js +0 -889
- package/dist/chunk-YUE6TJ4M.js.map +0 -1
- package/dist/chunk-ZGLJM4A4.js +0 -53
- package/dist/chunk-ZGLJM4A4.js.map +0 -1
- package/dist/function-CxixTB02.d.cts +0 -299
- package/dist/function-DTCt675z.d.ts +0 -299
- package/dist/index-B2Ulxxpo.d.cts +0 -144
- package/dist/index-BkYJ0xMq.d.cts +0 -152
- package/dist/index-BuaB-ypt.d.ts +0 -144
- package/dist/index-CAN7v-kw.d.ts +0 -199
- package/dist/index-COw1Ky9g.d.ts +0 -242
- package/dist/index-DRtIhlJj.d.ts +0 -152
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-alrRninU.d.cts +0 -199
- package/dist/index-oTQbYYIs.d.cts +0 -242
- package/dist/index-tR9jTn26.d.cts +0 -275
- package/dist/index-tR9jTn26.d.ts +0 -275
- package/dist/styled-Bl0ZK28h.d.ts +0 -543
- package/dist/styled-Crv6SA4S.d.cts +0 -543
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
package/dist/chunk-4VZ5MZAM.cjs
DELETED
|
@@ -1,889 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var _chunk2A2QMA74cjs = require('./chunk-2A2QMA74.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var _chunkKQN2ZYNDcjs = require('./chunk-KQN2ZYND.cjs');
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var _chunkWFDXOLUBcjs = require('./chunk-WFDXOLUB.cjs');
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
|
|
37
|
-
|
|
38
|
-
// src/analyze/timeline/index.ts
|
|
39
|
-
function chordTimelineFromChords(chords, totalBeats) {
|
|
40
|
-
_chunkPI3UXRWKcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
|
|
41
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, chords.length, "timeline chords");
|
|
42
|
-
for (let index = 0; index < chords.length; index += 1) {
|
|
43
|
-
_chunkPI3UXRWKcjs.assertRange.call(void 0,
|
|
44
|
-
_nullishCoalesce(_optionalChain([chords, 'access', _ => _[index], 'optionalAccess', _2 => _2.startBeat]), () => ( Number.NaN)),
|
|
45
|
-
0,
|
|
46
|
-
Number.MAX_SAFE_INTEGER,
|
|
47
|
-
`chords[${index}].startBeat`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
const sorted = [...chords].filter((chord) => chord.startBeat < totalBeats).sort((a, b) => a.startBeat - b.startBeat);
|
|
51
|
-
const segments = sorted.map((gc, i) => {
|
|
52
|
-
const next = sorted[i + 1];
|
|
53
|
-
const nextBeat = next ? next.startBeat : totalBeats;
|
|
54
|
-
const endBeat = Math.min(totalBeats, Math.max(gc.startBeat, nextBeat));
|
|
55
|
-
return {
|
|
56
|
-
startBeat: gc.startBeat,
|
|
57
|
-
endBeat,
|
|
58
|
-
chord: _chunkBSLTLGM3cjs.makeChord.call(void 0, gc.rootPc, gc.quality, gc.bassPc)
|
|
59
|
-
};
|
|
60
|
-
}).filter((seg) => seg.endBeat > seg.startBeat);
|
|
61
|
-
return { at: segmentLookup(segments), segments };
|
|
62
|
-
}
|
|
63
|
-
function segmentLookup(segments) {
|
|
64
|
-
return (beat) => {
|
|
65
|
-
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, beat, "timeline query beat");
|
|
66
|
-
let low = 0;
|
|
67
|
-
let high = segments.length;
|
|
68
|
-
while (low < high) {
|
|
69
|
-
const middle = Math.floor((low + high) / 2);
|
|
70
|
-
if ((_nullishCoalesce(_optionalChain([segments, 'access', _3 => _3[middle], 'optionalAccess', _4 => _4.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
|
|
71
|
-
low = middle + 1;
|
|
72
|
-
} else {
|
|
73
|
-
high = middle;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const candidate = segments[low - 1];
|
|
77
|
-
return candidate !== void 0 && beat >= candidate.startBeat && beat < candidate.endBeat ? candidate.chord : null;
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
var EPS = 1e-9;
|
|
81
|
-
var NOISE_THRESHOLD_RATIO = 0.2;
|
|
82
|
-
var MAX_DETECTION_PCS = 6;
|
|
83
|
-
var INEXACT_CONFIDENCE_FACTOR = 0.85;
|
|
84
|
-
var DIATONIC_BONUS = 0.5;
|
|
85
|
-
var EXACT_BONUS = 0.5;
|
|
86
|
-
var MISMATCH_PENALTY = 0.3;
|
|
87
|
-
function scoreMatch(match, weights, maxWeight, key) {
|
|
88
|
-
const rootWeight = maxWeight > 0 ? (_nullishCoalesce(weights[match.rootPc], () => ( 0))) / maxWeight : 0;
|
|
89
|
-
const tones = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, match.rootPc, match.quality));
|
|
90
|
-
let score = rootWeight;
|
|
91
|
-
if (tones.every((pc) => _chunkBSLTLGM3cjs.isScaleTone.call(void 0, pc, key))) {
|
|
92
|
-
score += DIATONIC_BONUS;
|
|
93
|
-
}
|
|
94
|
-
if (match.exact) {
|
|
95
|
-
score += EXACT_BONUS;
|
|
96
|
-
}
|
|
97
|
-
score -= MISMATCH_PENALTY * (match.extraPcs.length + match.missingPcs.length);
|
|
98
|
-
return score;
|
|
99
|
-
}
|
|
100
|
-
function chordConfidence(chord, weights, totalWeight, exact) {
|
|
101
|
-
const tones = new Set(_chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord));
|
|
102
|
-
let chordWeight = 0;
|
|
103
|
-
for (let pc = 0; pc < 12; pc += 1) {
|
|
104
|
-
if (tones.has(pc)) {
|
|
105
|
-
chordWeight += _nullishCoalesce(weights[pc], () => ( 0));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const raw = totalWeight > 0 ? chordWeight / totalWeight : 0;
|
|
109
|
-
const confidence = Math.min(1, Math.max(0, raw));
|
|
110
|
-
return exact ? confidence : confidence * INEXACT_CONFIDENCE_FACTOR;
|
|
111
|
-
}
|
|
112
|
-
function analyzeWindow(notes, windowStart, windowEnd, ts, key) {
|
|
113
|
-
const weights = new Array(12).fill(0);
|
|
114
|
-
let lowestPitch = Number.POSITIVE_INFINITY;
|
|
115
|
-
for (const note of notes) {
|
|
116
|
-
const noteEnd = note.startBeat + note.durationBeat;
|
|
117
|
-
const overlap = Math.min(noteEnd, windowEnd) - Math.max(note.startBeat, windowStart);
|
|
118
|
-
if (overlap <= EPS) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
const velocityFactor = note.velocity !== void 0 ? note.velocity / 127 : 1;
|
|
122
|
-
const onsetInWindow = note.startBeat >= windowStart - EPS && note.startBeat < windowEnd - EPS;
|
|
123
|
-
const accent = onsetInWindow ? 1 + _chunk2A2QMA74cjs.metricWeight.call(void 0, note.startBeat, ts) / 3 : 1;
|
|
124
|
-
const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, note.pitch);
|
|
125
|
-
weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + overlap * velocityFactor * accent;
|
|
126
|
-
if (note.pitch < lowestPitch) {
|
|
127
|
-
lowestPitch = note.pitch;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
let totalWeight = 0;
|
|
131
|
-
let maxWeight = 0;
|
|
132
|
-
for (const w of weights) {
|
|
133
|
-
totalWeight += w;
|
|
134
|
-
maxWeight = Math.max(maxWeight, w);
|
|
135
|
-
}
|
|
136
|
-
if (totalWeight <= EPS) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const ranked = weights.map((weight, pc) => ({ pc, weight })).filter(({ weight }) => weight > 0).sort((a, b) => b.weight - a.weight || a.pc - b.pc);
|
|
140
|
-
const selected = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
|
|
141
|
-
const bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, lowestPitch);
|
|
142
|
-
const hasBass = selected.includes(bassPc);
|
|
143
|
-
const detectionPitches = hasBass ? selected.map((pc) => pc === bassPc ? pc : pc + 12) : selected;
|
|
144
|
-
const matches = _chunkKQN2ZYNDcjs.detectChord.call(void 0, detectionPitches, { input: hasBass ? "midi" : "pitchClass" });
|
|
145
|
-
let bestMatch = null;
|
|
146
|
-
let bestScore = Number.NEGATIVE_INFINITY;
|
|
147
|
-
for (const match of matches) {
|
|
148
|
-
const score = scoreMatch(match, weights, maxWeight, key);
|
|
149
|
-
if (score > bestScore) {
|
|
150
|
-
bestScore = score;
|
|
151
|
-
bestMatch = match;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (bestMatch) {
|
|
155
|
-
const chord = _chunkBSLTLGM3cjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality, bestMatch.bassPc);
|
|
156
|
-
return { chord, confidence: chordConfidence(chord, weights, totalWeight, bestMatch.exact) };
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
function sameChord(a, b) {
|
|
161
|
-
return a.rootPc === b.rootPc && a.quality === b.quality && a.bassPc === b.bassPc;
|
|
162
|
-
}
|
|
163
|
-
function chordTimelineFromNotes(notes, opts = {}) {
|
|
164
|
-
const ts = _nullishCoalesce(opts.ts, () => ( _chunk2A2QMA74cjs.parseTimeSignature.call(void 0, "4/4")));
|
|
165
|
-
_chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
|
|
166
|
-
const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => ( _chunk2A2QMA74cjs.beatsPerBar.call(void 0, ts)));
|
|
167
|
-
_chunkPI3UXRWKcjs.assertRange.call(void 0, harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
|
|
168
|
-
const budget = opts.budget;
|
|
169
|
-
_chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, notes, "timeline notes", { allowNonPositiveDuration: true, budget });
|
|
170
|
-
const soundingIndex = _chunk2A2QMA74cjs.createNoteEventIndex.call(void 0,
|
|
171
|
-
notes.filter((note) => note.durationBeat > 0),
|
|
172
|
-
{ name: "timeline notes", budget }
|
|
173
|
-
);
|
|
174
|
-
const sounding = soundingIndex.notes.map(({ note }) => note);
|
|
175
|
-
const lastNoteEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
|
|
176
|
-
const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastNoteEnd));
|
|
177
|
-
_chunkPI3UXRWKcjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
|
|
178
|
-
const key = _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkKQN2ZYNDcjs.detectKeyFromNotes.call(void 0, sounding, { budget }), 'access', _5 => _5[0], 'optionalAccess', _6 => _6.key]))), () => ( _chunkBSLTLGM3cjs.majorKey.call(void 0, 0)));
|
|
179
|
-
const segments = [];
|
|
180
|
-
const segmentConfidence = [];
|
|
181
|
-
const windowCount = Math.max(0, Math.ceil(totalBeats / harmonicRhythm - EPS));
|
|
182
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, windowCount, "timeline windows", budget);
|
|
183
|
-
const windowNotes = Array.from({ length: windowCount }, () => []);
|
|
184
|
-
let memberships = 0;
|
|
185
|
-
for (const indexed of soundingIndex.notes) {
|
|
186
|
-
const first = Math.max(0, Math.floor(indexed.note.startBeat / harmonicRhythm));
|
|
187
|
-
const lastExclusive = Math.min(windowCount, Math.ceil(indexed.endBeat / harmonicRhythm));
|
|
188
|
-
memberships += Math.max(0, lastExclusive - first);
|
|
189
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, memberships, "timeline note-to-window memberships", budget);
|
|
190
|
-
for (let window = first; window < lastExclusive; window += 1) {
|
|
191
|
-
_optionalChain([windowNotes, 'access', _7 => _7[window], 'optionalAccess', _8 => _8.push, 'call', _9 => _9(indexed.note)]);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
for (let i = 0; i < windowCount; i += 1) {
|
|
195
|
-
const start = i * harmonicRhythm;
|
|
196
|
-
const end = Math.min(start + harmonicRhythm, totalBeats);
|
|
197
|
-
const inferred = analyzeWindow(_nullishCoalesce(windowNotes[i], () => ( [])), start, end, ts, key);
|
|
198
|
-
if (!inferred) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const last = segments[segments.length - 1];
|
|
202
|
-
if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) < EPS) {
|
|
203
|
-
const lastLength = last.endBeat - last.startBeat;
|
|
204
|
-
const length = end - start;
|
|
205
|
-
const lastConfidence = _nullishCoalesce(segmentConfidence[segmentConfidence.length - 1], () => ( 0));
|
|
206
|
-
segmentConfidence[segmentConfidence.length - 1] = (lastConfidence * lastLength + inferred.confidence * length) / (lastLength + length);
|
|
207
|
-
last.endBeat = end;
|
|
208
|
-
} else {
|
|
209
|
-
segments.push({ startBeat: start, endBeat: end, chord: inferred.chord });
|
|
210
|
-
segmentConfidence.push(inferred.confidence);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
timeline: { at: segmentLookup(segments), segments },
|
|
215
|
-
key,
|
|
216
|
-
segmentConfidence
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function detectCadences(timeline, key) {
|
|
220
|
-
const hits = [];
|
|
221
|
-
for (let i = 1; i < timeline.segments.length; i += 1) {
|
|
222
|
-
const prev = timeline.segments[i - 1];
|
|
223
|
-
const cur = timeline.segments[i];
|
|
224
|
-
if (!prev || !cur) {
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
if (Math.abs(cur.startBeat - prev.endBeat) > EPS) {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
const type = _chunkKQN2ZYNDcjs.detectCadence.call(void 0, prev.chord, cur.chord, key);
|
|
231
|
-
if (type !== null) {
|
|
232
|
-
hits.push({ atBeat: cur.startBeat, type, from: prev.chord, to: cur.chord });
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return hits;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// src/analyze/arrange/internal.ts
|
|
239
|
-
var EPS2 = 1e-9;
|
|
240
|
-
var MAX_LANE_LEAP = 24;
|
|
241
|
-
function covers(note, beat) {
|
|
242
|
-
return beat >= note.startBeat - EPS2 && beat < note.endBeat - EPS2;
|
|
243
|
-
}
|
|
244
|
-
function isPercussion(role) {
|
|
245
|
-
return role === "drums";
|
|
246
|
-
}
|
|
247
|
-
function poolNotes(tracks, only) {
|
|
248
|
-
const notes = [];
|
|
249
|
-
for (let index = 0; index < tracks.length; index += 1) {
|
|
250
|
-
const track = tracks[index];
|
|
251
|
-
if (track === void 0 || isPercussion(track.role)) {
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
if (only !== void 0 && !only.has(index)) {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
for (const note of track.notes) {
|
|
258
|
-
if (note.durationBeat > 0) {
|
|
259
|
-
notes.push(note);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return notes;
|
|
264
|
-
}
|
|
265
|
-
function splitIntoSubVoices(ordered) {
|
|
266
|
-
const lanes = [];
|
|
267
|
-
const attachNearest = (note, excluded = /* @__PURE__ */ new Set()) => {
|
|
268
|
-
let best = -1;
|
|
269
|
-
let bestDistance = Number.POSITIVE_INFINITY;
|
|
270
|
-
let bestEnd = Number.NEGATIVE_INFINITY;
|
|
271
|
-
for (let lane = 0; lane < lanes.length; lane += 1) {
|
|
272
|
-
if (excluded.has(lane)) {
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
const last = _optionalChain([lanes, 'access', _10 => _10[lane], 'optionalAccess', _11 => _11[(_nullishCoalesce(_optionalChain([lanes, 'access', _12 => _12[lane], 'optionalAccess', _13 => _13.length]), () => ( 0))) - 1]]);
|
|
276
|
-
if (last === void 0) {
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
const end = last.startBeat + last.durationBeat;
|
|
280
|
-
if (end > note.startBeat + EPS2) {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
const distance = Math.abs(last.pitch - note.pitch);
|
|
284
|
-
if (distance > MAX_LANE_LEAP) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (distance < bestDistance || distance === bestDistance && end > bestEnd) {
|
|
288
|
-
best = lane;
|
|
289
|
-
bestDistance = distance;
|
|
290
|
-
bestEnd = end;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
if (best >= 0) {
|
|
294
|
-
_optionalChain([lanes, 'access', _14 => _14[best], 'optionalAccess', _15 => _15.push, 'call', _16 => _16(note)]);
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
return false;
|
|
298
|
-
};
|
|
299
|
-
for (let start = 0; start < ordered.length; ) {
|
|
300
|
-
const onset = _nullishCoalesce(_optionalChain([ordered, 'access', _17 => _17[start], 'optionalAccess', _18 => _18.startBeat]), () => ( 0));
|
|
301
|
-
let end = start + 1;
|
|
302
|
-
while (end < ordered.length && Math.abs((_nullishCoalesce(_optionalChain([ordered, 'access', _19 => _19[end], 'optionalAccess', _20 => _20.startBeat]), () => ( 0))) - onset) <= EPS2) {
|
|
303
|
-
end += 1;
|
|
304
|
-
}
|
|
305
|
-
const block = ordered.slice(start, end);
|
|
306
|
-
if (block.length > 1) {
|
|
307
|
-
const freeLanes = lanes.map((lane, index) => ({ index, last: lane[lane.length - 1] })).filter(
|
|
308
|
-
(entry) => entry.last !== void 0 && entry.last.startBeat + entry.last.durationBeat <= onset + EPS2
|
|
309
|
-
).sort((a, b) => a.last.pitch - b.last.pitch);
|
|
310
|
-
const paired = /* @__PURE__ */ new Set();
|
|
311
|
-
for (let index = 0; index < block.length; index += 1) {
|
|
312
|
-
const note = block[index];
|
|
313
|
-
const lane = freeLanes[index];
|
|
314
|
-
if (note !== void 0 && lane !== void 0 && Math.abs(lane.last.pitch - note.pitch) <= MAX_LANE_LEAP) {
|
|
315
|
-
_optionalChain([lanes, 'access', _21 => _21[lane.index], 'optionalAccess', _22 => _22.push, 'call', _23 => _23(note)]);
|
|
316
|
-
paired.add(lane.index);
|
|
317
|
-
} else if (note !== void 0 && !attachNearest(note, paired)) {
|
|
318
|
-
lanes.push([note]);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
} else {
|
|
322
|
-
const note = block[0];
|
|
323
|
-
if (note !== void 0 && !attachNearest(note)) {
|
|
324
|
-
lanes.push([note]);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
start = end;
|
|
328
|
-
}
|
|
329
|
-
return lanes;
|
|
330
|
-
}
|
|
331
|
-
function lastStartingAtOrBefore(sounding, beat) {
|
|
332
|
-
let low = 0;
|
|
333
|
-
let high = sounding.length;
|
|
334
|
-
while (low < high) {
|
|
335
|
-
const middle = Math.floor((low + high) / 2);
|
|
336
|
-
if ((_nullishCoalesce(_optionalChain([sounding, 'access', _24 => _24[middle], 'optionalAccess', _25 => _25.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + EPS2) {
|
|
337
|
-
low = middle + 1;
|
|
338
|
-
} else {
|
|
339
|
-
high = middle;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return low - 1;
|
|
343
|
-
}
|
|
344
|
-
function prepareTracks(tracks) {
|
|
345
|
-
let nextId = 0;
|
|
346
|
-
return tracks.map((track, index) => {
|
|
347
|
-
const ordered = track.notes.map((note, originalIndex) => ({ note, originalIndex })).filter(({ note }) => note.durationBeat > 0).sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch).map(({ note, originalIndex }) => ({ ...note, id: nextId++, originalIndex }));
|
|
348
|
-
const voices = splitIntoSubVoices(ordered).map((voice) => {
|
|
349
|
-
const sounding = voice.map((note, i) => {
|
|
350
|
-
const prev = i > 0 ? voice[i - 1] : void 0;
|
|
351
|
-
const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
|
|
352
|
-
const contiguous = prevEnd !== void 0 && Math.abs(prevEnd - note.startBeat) <= EPS2;
|
|
353
|
-
return {
|
|
354
|
-
pitch: note.pitch,
|
|
355
|
-
prevPitch: contiguous ? _optionalChain([prev, 'optionalAccess', _26 => _26.pitch]) : void 0,
|
|
356
|
-
startBeat: note.startBeat,
|
|
357
|
-
endBeat: note.startBeat + note.durationBeat
|
|
358
|
-
};
|
|
359
|
-
});
|
|
360
|
-
return {
|
|
361
|
-
voice,
|
|
362
|
-
sounding,
|
|
363
|
-
at: (beat) => {
|
|
364
|
-
const candidate = sounding[lastStartingAtOrBefore(sounding, beat)];
|
|
365
|
-
return candidate !== void 0 && covers(candidate, beat) ? candidate : void 0;
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
});
|
|
369
|
-
return {
|
|
370
|
-
name: _nullishCoalesce(track.name, () => ( `track ${index + 1}`)),
|
|
371
|
-
role: _nullishCoalesce(track.role, () => ( "other")),
|
|
372
|
-
trackIndex: index,
|
|
373
|
-
voices
|
|
374
|
-
};
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// src/analyze/arrange/tension.ts
|
|
379
|
-
var FUNCTION_TENSION = {
|
|
380
|
-
tonic: 0,
|
|
381
|
-
subdominant: 0.5,
|
|
382
|
-
dominant: 1
|
|
383
|
-
};
|
|
384
|
-
var FUNCTION_WEIGHT = 0.5;
|
|
385
|
-
var DISSONANCE_WEIGHT = 0.35;
|
|
386
|
-
var SPAN_WEIGHT = 0.15;
|
|
387
|
-
var SPAN_SATURATION = 24;
|
|
388
|
-
function clamp01(value) {
|
|
389
|
-
return Math.min(1, Math.max(0, value));
|
|
390
|
-
}
|
|
391
|
-
function tensionCurve(tracks, opts = {}) {
|
|
392
|
-
const ts = _nullishCoalesce(opts.ts, () => ( _chunk2A2QMA74cjs.parseTimeSignature.call(void 0, "4/4")));
|
|
393
|
-
_chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
|
|
394
|
-
const budget = opts.budget;
|
|
395
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
|
|
396
|
-
for (let index = 0; index < tracks.length; index += 1) {
|
|
397
|
-
_chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, _nullishCoalesce(_optionalChain([tracks, 'access', _27 => _27[index], 'optionalAccess', _28 => _28.notes]), () => ( [])), `tracks[${index}].notes`, {
|
|
398
|
-
allowNonPositiveDuration: true,
|
|
399
|
-
budget
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
const profile = _nullishCoalesce(opts.profile, () => ( "pop"));
|
|
403
|
-
const step = _nullishCoalesce(opts.step, () => ( 1));
|
|
404
|
-
_chunkPI3UXRWKcjs.assertRange.call(void 0, step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
|
|
405
|
-
const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
|
|
406
|
-
const pooled = poolNotes(tracks, harmonyTracks);
|
|
407
|
-
const totalBeats = poolNotes(tracks).reduce(
|
|
408
|
-
(end, n) => Math.max(end, n.startBeat + n.durationBeat),
|
|
409
|
-
0
|
|
410
|
-
);
|
|
411
|
-
const { timeline, key } = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
|
|
412
|
-
key: opts.key,
|
|
413
|
-
ts,
|
|
414
|
-
harmonicRhythm: opts.harmonicRhythm,
|
|
415
|
-
budget
|
|
416
|
-
}) : {
|
|
417
|
-
timeline: opts.timeline,
|
|
418
|
-
key: _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkKQN2ZYNDcjs.detectKeyFromNotes.call(void 0, pooled, { budget }), 'access', _29 => _29[0], 'optionalAccess', _30 => _30.key]))), () => ( _chunkBSLTLGM3cjs.majorKey.call(void 0, 0)))
|
|
419
|
-
};
|
|
420
|
-
const prepared = prepareTracks(tracks);
|
|
421
|
-
const points = [];
|
|
422
|
-
const sampleCount = Math.max(0, Math.ceil(totalBeats / step - EPS2));
|
|
423
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, sampleCount, "tension samples", budget);
|
|
424
|
-
const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
|
|
425
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0,
|
|
426
|
-
sampleCount * Math.min(128, voiceCount) * voiceCount,
|
|
427
|
-
"tension sample-voice comparisons",
|
|
428
|
-
budget
|
|
429
|
-
);
|
|
430
|
-
for (let i = 0; i < sampleCount; i += 1) {
|
|
431
|
-
const beat = i * step;
|
|
432
|
-
points.push({ beat, tension: sampleTension(prepared, timeline, key, ts, profile, beat) });
|
|
433
|
-
}
|
|
434
|
-
return points;
|
|
435
|
-
}
|
|
436
|
-
function tensionCurveFrom(tracks, analysis, opts = {}) {
|
|
437
|
-
return tensionCurve(tracks, { ...opts, timeline: analysis.timeline, key: analysis.key });
|
|
438
|
-
}
|
|
439
|
-
function allSounding(prepared, beat) {
|
|
440
|
-
const out = [];
|
|
441
|
-
for (let t = 0; t < prepared.length; t += 1) {
|
|
442
|
-
const track = prepared[t];
|
|
443
|
-
if (!track || isPercussion(track.role)) {
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
for (const subVoice of track.voices) {
|
|
447
|
-
const note = subVoice.at(beat);
|
|
448
|
-
if (note !== void 0) {
|
|
449
|
-
out.push({ pitch: note.pitch, track: t });
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return out;
|
|
454
|
-
}
|
|
455
|
-
function sampleTension(prepared, timeline, key, ts, profile, beat) {
|
|
456
|
-
const sounding = allSounding(prepared, beat);
|
|
457
|
-
if (sounding.length === 0) {
|
|
458
|
-
return 0;
|
|
459
|
-
}
|
|
460
|
-
const chord = timeline.at(beat);
|
|
461
|
-
const functionScore = chord ? FUNCTION_TENSION[_chunkKQN2ZYNDcjs.functionOf.call(void 0, chord, key)] : 0;
|
|
462
|
-
const chordPcs = chord ? new Set(_chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord)) : null;
|
|
463
|
-
const strongBeat = _chunk2A2QMA74cjs.isStrongBeat.call(void 0, beat, ts);
|
|
464
|
-
let nonChord = 0;
|
|
465
|
-
let dissonant = 0;
|
|
466
|
-
const pitches = /* @__PURE__ */ new Map();
|
|
467
|
-
for (const voice of sounding) {
|
|
468
|
-
pitches.set(voice.pitch, (_nullishCoalesce(pitches.get(voice.pitch), () => ( 0))) + 1);
|
|
469
|
-
}
|
|
470
|
-
for (const [pitch, count] of pitches) {
|
|
471
|
-
if (chordPcs && !chordPcs.has(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch))) {
|
|
472
|
-
nonChord += count;
|
|
473
|
-
}
|
|
474
|
-
const others = [...pitches].flatMap(
|
|
475
|
-
([otherPitch, otherCount]) => Array.from({ length: otherCount - (otherPitch === pitch ? 1 : 0) }, () => ({
|
|
476
|
-
pitch: otherPitch
|
|
477
|
-
}))
|
|
478
|
-
);
|
|
479
|
-
const result = _chunkWFDXOLUBcjs.evaluateSafety.call(void 0,
|
|
480
|
-
{
|
|
481
|
-
profile,
|
|
482
|
-
candidatePitch: pitch,
|
|
483
|
-
chord,
|
|
484
|
-
key,
|
|
485
|
-
otherVoices: others,
|
|
486
|
-
strongBeat
|
|
487
|
-
},
|
|
488
|
-
{ suggestions: false }
|
|
489
|
-
);
|
|
490
|
-
if (result.safety === 2 /* Dissonant */) {
|
|
491
|
-
dissonant += count;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
const dissonanceScore = Math.max(nonChord, dissonant) / sounding.length;
|
|
495
|
-
let low = Number.POSITIVE_INFINITY;
|
|
496
|
-
let high = Number.NEGATIVE_INFINITY;
|
|
497
|
-
for (const voice of sounding) {
|
|
498
|
-
low = Math.min(low, voice.pitch);
|
|
499
|
-
high = Math.max(high, voice.pitch);
|
|
500
|
-
}
|
|
501
|
-
const spanScore = Math.min(1, (high - low) / SPAN_SATURATION);
|
|
502
|
-
return clamp01(
|
|
503
|
-
FUNCTION_WEIGHT * functionScore + DISSONANCE_WEIGHT * dissonanceScore + SPAN_WEIGHT * spanScore
|
|
504
|
-
);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// src/analyze/voice/index.ts
|
|
508
|
-
function toVoiceNotes(events) {
|
|
509
|
-
return events.map((event, index) => ({ ...event, id: index, originalIndex: index }));
|
|
510
|
-
}
|
|
511
|
-
var HUMANIZE_ADJACENCY = 0.05;
|
|
512
|
-
function intervalAboveBass(pitch, others, chord) {
|
|
513
|
-
let bass = Number.POSITIVE_INFINITY;
|
|
514
|
-
for (const ov of others) {
|
|
515
|
-
bass = Math.min(bass, ov.pitch);
|
|
516
|
-
}
|
|
517
|
-
if (Number.isFinite(bass) && bass < pitch) {
|
|
518
|
-
return ((pitch - bass) % 12 + 12) % 12;
|
|
519
|
-
}
|
|
520
|
-
const ref = _nullishCoalesce(chord.bassPc, () => ( chord.rootPc));
|
|
521
|
-
return ((_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch) - _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, ref)) % 12 + 12) % 12;
|
|
522
|
-
}
|
|
523
|
-
function tensionDegree(ic) {
|
|
524
|
-
if (ic === 1 || ic === 2) {
|
|
525
|
-
return 9;
|
|
526
|
-
}
|
|
527
|
-
if (ic === 5 || ic === 6) {
|
|
528
|
-
return 11;
|
|
529
|
-
}
|
|
530
|
-
return 13;
|
|
531
|
-
}
|
|
532
|
-
function isStep(a, b) {
|
|
533
|
-
const d = Math.abs(a - b);
|
|
534
|
-
return d === 1 || d === 2;
|
|
535
|
-
}
|
|
536
|
-
function suspensionType(ic, delta) {
|
|
537
|
-
if (delta > 0) {
|
|
538
|
-
return "sus2-3";
|
|
539
|
-
}
|
|
540
|
-
if (ic === 2) {
|
|
541
|
-
return "sus9-8";
|
|
542
|
-
}
|
|
543
|
-
if (ic === 10 || ic === 11) {
|
|
544
|
-
return "sus7-6";
|
|
545
|
-
}
|
|
546
|
-
if (ic === 5 || ic === 6) {
|
|
547
|
-
return "sus4-3";
|
|
548
|
-
}
|
|
549
|
-
if (ic === 8 || ic === 9) {
|
|
550
|
-
return "sus6-5";
|
|
551
|
-
}
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
function stepResolution(pitch, chord) {
|
|
555
|
-
for (let delta = 1; delta <= 2; delta += 1) {
|
|
556
|
-
if (_chunkBSLTLGM3cjs.isChordMember.call(void 0, pitch - delta, chord)) {
|
|
557
|
-
return pitch - delta;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
return void 0;
|
|
561
|
-
}
|
|
562
|
-
function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat = () => []) {
|
|
563
|
-
_chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, voice, "voice notes", { allowNonPositiveDuration: true });
|
|
564
|
-
const result = [];
|
|
565
|
-
for (let i = 0; i < voice.length; i += 1) {
|
|
566
|
-
const note = voice[i];
|
|
567
|
-
if (!note) {
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
const prevNote = i > 0 ? voice[i - 1] : void 0;
|
|
571
|
-
const nextNote = i + 1 < voice.length ? voice[i + 1] : void 0;
|
|
572
|
-
const prevEnd = prevNote === void 0 ? Number.NEGATIVE_INFINITY : prevNote.startBeat + prevNote.durationBeat;
|
|
573
|
-
const noteEnd = note.startBeat + note.durationBeat;
|
|
574
|
-
const prev = prevNote !== void 0 && Math.abs(prevEnd - note.startBeat) <= HUMANIZE_ADJACENCY ? prevNote : void 0;
|
|
575
|
-
const next = nextNote !== void 0 && Math.abs(noteEnd - nextNote.startBeat) <= HUMANIZE_ADJACENCY ? nextNote : void 0;
|
|
576
|
-
const chord = chordAtBeat(note.startBeat);
|
|
577
|
-
const labels = [];
|
|
578
|
-
const member = _chunkBSLTLGM3cjs.isChordMember.call(void 0, note.pitch, chord);
|
|
579
|
-
let handled = false;
|
|
580
|
-
if (chord && member) {
|
|
581
|
-
const role = _chunkBSLTLGM3cjs.chordToneRole.call(void 0, note.pitch, chord);
|
|
582
|
-
if (role) {
|
|
583
|
-
labels.push({ kind: "chordTone", role });
|
|
584
|
-
} else {
|
|
585
|
-
const ic = _chunkBSLTLGM3cjs.intervalAboveRoot.call(void 0, note.pitch, chord);
|
|
586
|
-
labels.push({ kind: "tension", degree: tensionDegree(ic) });
|
|
587
|
-
}
|
|
588
|
-
handled = true;
|
|
589
|
-
}
|
|
590
|
-
if (!handled && chord && prev && next) {
|
|
591
|
-
const prevChord = chordAtBeat(prev.startBeat);
|
|
592
|
-
const prepared = prev.pitch === note.pitch && _chunkBSLTLGM3cjs.isChordMember.call(void 0, prev.pitch, prevChord);
|
|
593
|
-
const resolves = isStep(next.pitch, note.pitch);
|
|
594
|
-
const others = otherVoicesAtBeat(note.startBeat);
|
|
595
|
-
const verticallyDissonant = others.some(
|
|
596
|
-
(ov) => _chunkBSLTLGM3cjs.createsVerticalDissonance.call(void 0, note.pitch, ov.pitch, true)
|
|
597
|
-
);
|
|
598
|
-
if (prepared && verticallyDissonant && resolves) {
|
|
599
|
-
const ic = intervalAboveBass(note.pitch, others, chord);
|
|
600
|
-
const type = suspensionType(ic, next.pitch - note.pitch);
|
|
601
|
-
if (type !== null) {
|
|
602
|
-
labels.push({ kind: "suspension", type, resolveTo: next.pitch });
|
|
603
|
-
handled = true;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
if (!handled && chord && prev && next) {
|
|
608
|
-
const prevMember = _chunkBSLTLGM3cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat));
|
|
609
|
-
const nextMember = _chunkBSLTLGM3cjs.isChordMember.call(void 0, next.pitch, chordAtBeat(next.startBeat));
|
|
610
|
-
const up = note.pitch - prev.pitch > 0;
|
|
611
|
-
const contInto = next.pitch - note.pitch > 0;
|
|
612
|
-
const sameDir = note.pitch !== prev.pitch && next.pitch !== note.pitch && up === contInto;
|
|
613
|
-
if (prevMember && nextMember && !member && isStep(note.pitch, prev.pitch) && isStep(next.pitch, note.pitch) && sameDir) {
|
|
614
|
-
labels.push({ kind: "passing" });
|
|
615
|
-
handled = true;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
if (!handled && chord && prev && next && !member && prev.pitch === next.pitch && _chunkBSLTLGM3cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat)) && isStep(note.pitch, prev.pitch)) {
|
|
619
|
-
labels.push({ kind: "neighbor" });
|
|
620
|
-
handled = true;
|
|
621
|
-
}
|
|
622
|
-
if (!handled && chord && next && !member && note.pitch === next.pitch) {
|
|
623
|
-
const nextChord = chordAtBeat(next.startBeat);
|
|
624
|
-
if (_chunkBSLTLGM3cjs.isChordMember.call(void 0, next.pitch, nextChord)) {
|
|
625
|
-
labels.push({ kind: "anticipation" });
|
|
626
|
-
handled = true;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
if (!handled && chord && prev && next && !member) {
|
|
630
|
-
const prevMember = _chunkBSLTLGM3cjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat));
|
|
631
|
-
const nextMember = _chunkBSLTLGM3cjs.isChordMember.call(void 0, next.pitch, chordAtBeat(next.startBeat));
|
|
632
|
-
const stepFromPrev = isStep(note.pitch, prev.pitch);
|
|
633
|
-
const leapToNext = Math.abs(next.pitch - note.pitch) >= 3;
|
|
634
|
-
const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
|
|
635
|
-
if (prevMember && nextMember && stepFromPrev && leapToNext && opposite) {
|
|
636
|
-
labels.push({ kind: "escape" });
|
|
637
|
-
handled = true;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (!handled && chord && !member) {
|
|
641
|
-
const ic = _chunkBSLTLGM3cjs.intervalAboveRoot.call(void 0, note.pitch, chord);
|
|
642
|
-
const isTension = ic === 2 || ic === 5 || ic === 9;
|
|
643
|
-
const avoid = ic === 5 && chord.intervals.includes(4) && !chord.intervals.includes(5) || ic === 11 && chord.intervals.includes(4) && chord.intervals.includes(10);
|
|
644
|
-
if (avoid) {
|
|
645
|
-
labels.push({ kind: "avoid" });
|
|
646
|
-
const resolveTo = stepResolution(note.pitch, chord);
|
|
647
|
-
if (resolveTo !== void 0) {
|
|
648
|
-
labels.push({ kind: "needsResolution", resolveTo });
|
|
649
|
-
}
|
|
650
|
-
} else if (isTension) {
|
|
651
|
-
labels.push({ kind: "tension", degree: tensionDegree(ic) });
|
|
652
|
-
} else {
|
|
653
|
-
const resolveTo = stepResolution(note.pitch, chord);
|
|
654
|
-
if (resolveTo !== void 0) {
|
|
655
|
-
labels.push({ kind: "needsResolution", resolveTo });
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
if (next && _chunkBSLTLGM3cjs.isLeadingToneResolution.call(void 0, note.pitch, next.pitch, key)) {
|
|
660
|
-
labels.push({ kind: "leadingTone", resolveTo: next.pitch });
|
|
661
|
-
}
|
|
662
|
-
const analyzed = {
|
|
663
|
-
noteId: _nullishCoalesce(note.id, () => ( i)),
|
|
664
|
-
pitch: note.pitch,
|
|
665
|
-
startBeat: note.startBeat,
|
|
666
|
-
durationBeat: note.durationBeat,
|
|
667
|
-
labels,
|
|
668
|
-
rationale: describe(labels)
|
|
669
|
-
};
|
|
670
|
-
if (note.originalIndex !== void 0) {
|
|
671
|
-
analyzed.originalIndex = note.originalIndex;
|
|
672
|
-
}
|
|
673
|
-
result.push(analyzed);
|
|
674
|
-
}
|
|
675
|
-
return result;
|
|
676
|
-
}
|
|
677
|
-
function describe(labels) {
|
|
678
|
-
const primary = labels[0];
|
|
679
|
-
if (!primary) {
|
|
680
|
-
return "Unclassified note";
|
|
681
|
-
}
|
|
682
|
-
switch (primary.kind) {
|
|
683
|
-
case "chordTone":
|
|
684
|
-
return `Chord ${primary.role}`;
|
|
685
|
-
case "tension":
|
|
686
|
-
return `${primary.degree}th tension`;
|
|
687
|
-
case "avoid":
|
|
688
|
-
return "Avoid note";
|
|
689
|
-
case "passing":
|
|
690
|
-
return "Passing tone between chord tones";
|
|
691
|
-
case "neighbor":
|
|
692
|
-
return "Neighbor tone";
|
|
693
|
-
case "suspension":
|
|
694
|
-
return `Suspension (${primary.type})`;
|
|
695
|
-
case "anticipation":
|
|
696
|
-
return "Anticipation of the next chord";
|
|
697
|
-
case "escape":
|
|
698
|
-
return "Escape tone";
|
|
699
|
-
case "needsResolution":
|
|
700
|
-
return "Unresolved dissonance";
|
|
701
|
-
case "leadingTone":
|
|
702
|
-
return "Leading tone resolving to the tonic";
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// src/analyze/arrange/tracks.ts
|
|
707
|
-
function soundingVoicesAt(prepared, beat) {
|
|
708
|
-
const out = [];
|
|
709
|
-
for (let t = 0; t < prepared.length; t += 1) {
|
|
710
|
-
const track = prepared[t];
|
|
711
|
-
if (!track || isPercussion(track.role)) {
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
for (let v = 0; v < track.voices.length; v += 1) {
|
|
715
|
-
const note = _optionalChain([track, 'access', _31 => _31.voices, 'access', _32 => _32[v], 'optionalAccess', _33 => _33.at, 'call', _34 => _34(beat)]);
|
|
716
|
-
if (note === void 0) {
|
|
717
|
-
continue;
|
|
718
|
-
}
|
|
719
|
-
const snap = { pitch: note.pitch };
|
|
720
|
-
const previous = Math.abs(note.startBeat - beat) <= EPS2 ? note.prevPitch : note.pitch;
|
|
721
|
-
if (previous !== void 0) {
|
|
722
|
-
snap.prevPitch = previous;
|
|
723
|
-
}
|
|
724
|
-
out.push({ track: t, voice: v, snapshot: snap });
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
return out;
|
|
728
|
-
}
|
|
729
|
-
function otherVoicesSounding(prepared, excludeTrack, excludeVoice, beat, cache) {
|
|
730
|
-
const sounding = _nullishCoalesce(cache.get(beat), () => ( soundingVoicesAt(prepared, beat)));
|
|
731
|
-
cache.set(beat, sounding);
|
|
732
|
-
return sounding.filter(({ track, voice }) => track !== excludeTrack || voice !== excludeVoice).map(({ snapshot }) => snapshot);
|
|
733
|
-
}
|
|
734
|
-
function firstSegmentAfter(timeline, beat) {
|
|
735
|
-
let low = 0;
|
|
736
|
-
let high = timeline.segments.length;
|
|
737
|
-
while (low < high) {
|
|
738
|
-
const middle = Math.floor((low + high) / 2);
|
|
739
|
-
if ((_nullishCoalesce(_optionalChain([timeline, 'access', _35 => _35.segments, 'access', _36 => _36[middle], 'optionalAccess', _37 => _37.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + EPS2) {
|
|
740
|
-
low = middle + 1;
|
|
741
|
-
} else {
|
|
742
|
-
high = middle;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
return low;
|
|
746
|
-
}
|
|
747
|
-
function evaluationBeats(note, timeline) {
|
|
748
|
-
const noteEnd = note.startBeat + note.durationBeat;
|
|
749
|
-
const beats = [note.startBeat];
|
|
750
|
-
for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {
|
|
751
|
-
const start = _optionalChain([timeline, 'access', _38 => _38.segments, 'access', _39 => _39[i], 'optionalAccess', _40 => _40.startBeat]);
|
|
752
|
-
if (start === void 0 || start >= noteEnd - EPS2) {
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
beats.push(start);
|
|
756
|
-
}
|
|
757
|
-
return beats;
|
|
758
|
-
}
|
|
759
|
-
function analyzeArrangement(tracks, opts = {}) {
|
|
760
|
-
const ts = _nullishCoalesce(opts.ts, () => ( _chunk2A2QMA74cjs.parseTimeSignature.call(void 0, "4/4")));
|
|
761
|
-
_chunkPI3UXRWKcjs.assertTimeSignature.call(void 0, ts);
|
|
762
|
-
const budget = opts.budget;
|
|
763
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
|
|
764
|
-
let noteCount = 0;
|
|
765
|
-
for (let index = 0; index < tracks.length; index += 1) {
|
|
766
|
-
const notes = _optionalChain([tracks, 'access', _41 => _41[index], 'optionalAccess', _42 => _42.notes]);
|
|
767
|
-
if (!Array.isArray(notes)) {
|
|
768
|
-
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
|
|
769
|
-
`tracks[${index}].notes must be an array; received ${typeof notes}`
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
_chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, notes, `tracks[${index}].notes`, {
|
|
773
|
-
allowNonPositiveDuration: true,
|
|
774
|
-
budget
|
|
775
|
-
});
|
|
776
|
-
noteCount += notes.length;
|
|
777
|
-
}
|
|
778
|
-
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
|
|
779
|
-
const profile = _nullishCoalesce(opts.profile, () => ( "pop"));
|
|
780
|
-
const minSeverity = _nullishCoalesce(opts.minSeverity, () => ( 1)) /* Warning */;
|
|
781
|
-
const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
|
|
782
|
-
const pooled = poolNotes(tracks, harmonyTracks);
|
|
783
|
-
const inferred = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
|
|
784
|
-
key: opts.key,
|
|
785
|
-
ts,
|
|
786
|
-
harmonicRhythm: opts.harmonicRhythm,
|
|
787
|
-
budget
|
|
788
|
-
}) : {
|
|
789
|
-
timeline: opts.timeline,
|
|
790
|
-
key: _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkKQN2ZYNDcjs.detectKeyFromNotes.call(void 0, pooled, { budget }), 'access', _43 => _43[0], 'optionalAccess', _44 => _44.key]))), () => ( _chunkBSLTLGM3cjs.majorKey.call(void 0, 0))),
|
|
791
|
-
// A caller-provided timeline was not inferred from these notes, so
|
|
792
|
-
// it has no measured confidence. Reporting 1 would turn confidence
|
|
793
|
-
// gates into unconditional passes.
|
|
794
|
-
segmentConfidence: opts.timeline.segments.map(() => 0)
|
|
795
|
-
};
|
|
796
|
-
const { timeline, key, segmentConfidence } = inferred;
|
|
797
|
-
const cadences = detectCadences(timeline, key);
|
|
798
|
-
const prepared = prepareTracks(tracks);
|
|
799
|
-
const soundingCache = /* @__PURE__ */ new Map();
|
|
800
|
-
const trackAnalyses = [];
|
|
801
|
-
const conflicts = [];
|
|
802
|
-
for (let t = 0; t < prepared.length; t += 1) {
|
|
803
|
-
const track = prepared[t];
|
|
804
|
-
if (!track) {
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
if (isPercussion(track.role)) {
|
|
808
|
-
trackAnalyses.push({ name: track.name, role: track.role, notes: [] });
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
const notes = [];
|
|
812
|
-
for (let v = 0; v < track.voices.length; v += 1) {
|
|
813
|
-
const subVoice = track.voices[v];
|
|
814
|
-
if (!subVoice) {
|
|
815
|
-
continue;
|
|
816
|
-
}
|
|
817
|
-
const analyzed = analyzeVoice(
|
|
818
|
-
subVoice.voice,
|
|
819
|
-
timeline.at,
|
|
820
|
-
key,
|
|
821
|
-
(beat) => otherVoicesSounding(prepared, t, v, beat, soundingCache)
|
|
822
|
-
);
|
|
823
|
-
notes.push(...analyzed.map((note) => ({ ...note, trackIndex: track.trackIndex })));
|
|
824
|
-
const labelsById = new Map(analyzed.map((note) => [note.noteId, note.labels]));
|
|
825
|
-
for (let noteIndex = 0; noteIndex < subVoice.voice.length; noteIndex += 1) {
|
|
826
|
-
const note = subVoice.voice[noteIndex];
|
|
827
|
-
const preparedNote = subVoice.sounding[noteIndex];
|
|
828
|
-
if (note === void 0 || preparedNote === void 0) {
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
for (const beat of evaluationBeats(note, timeline)) {
|
|
832
|
-
const atOnset = Math.abs(beat - note.startBeat) <= EPS2;
|
|
833
|
-
const safetyQuery = {
|
|
834
|
-
profile,
|
|
835
|
-
candidatePitch: note.pitch,
|
|
836
|
-
prevPitch: atOnset ? preparedNote.prevPitch : note.pitch,
|
|
837
|
-
chord: timeline.at(beat),
|
|
838
|
-
key,
|
|
839
|
-
otherVoices: otherVoicesSounding(prepared, t, v, beat, soundingCache),
|
|
840
|
-
strongBeat: _chunk2A2QMA74cjs.isStrongBeat.call(void 0, beat, ts)
|
|
841
|
-
};
|
|
842
|
-
let result = _chunkWFDXOLUBcjs.evaluateSafety.call(void 0, safetyQuery, { suggestions: false });
|
|
843
|
-
if (result.safety !== 0 /* Safe */ && result.safety >= minSeverity) {
|
|
844
|
-
result = _chunkWFDXOLUBcjs.evaluateSafety.call(void 0, safetyQuery);
|
|
845
|
-
const conflict = {
|
|
846
|
-
beat,
|
|
847
|
-
trackName: track.name,
|
|
848
|
-
trackIndex: track.trackIndex,
|
|
849
|
-
noteId: note.id,
|
|
850
|
-
pitch: note.pitch,
|
|
851
|
-
labels: _nullishCoalesce(labelsById.get(note.id), () => ( [])),
|
|
852
|
-
safety: result.safety,
|
|
853
|
-
reasons: result.reasons
|
|
854
|
-
};
|
|
855
|
-
if (note.originalIndex !== void 0) {
|
|
856
|
-
conflict.originalIndex = note.originalIndex;
|
|
857
|
-
}
|
|
858
|
-
if (result.rationale !== void 0) {
|
|
859
|
-
conflict.rationale = result.rationale;
|
|
860
|
-
}
|
|
861
|
-
if (result.resolveTo !== void 0) {
|
|
862
|
-
conflict.resolveTo = result.resolveTo;
|
|
863
|
-
}
|
|
864
|
-
if (result.suggestions !== void 0) {
|
|
865
|
-
conflict.suggestions = [...result.suggestions];
|
|
866
|
-
}
|
|
867
|
-
conflicts.push(conflict);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
notes.sort((a, b) => a.noteId - b.noteId);
|
|
873
|
-
trackAnalyses.push({ name: track.name, role: track.role, notes });
|
|
874
|
-
}
|
|
875
|
-
conflicts.sort((a, b) => b.safety - a.safety || a.beat - b.beat);
|
|
876
|
-
return { key, timeline, segmentConfidence, cadences, tracks: trackAnalyses, conflicts };
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
exports.chordTimelineFromChords = chordTimelineFromChords; exports.chordTimelineFromNotes = chordTimelineFromNotes; exports.detectCadences = detectCadences; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.toVoiceNotes = toVoiceNotes; exports.analyzeVoice = analyzeVoice; exports.analyzeArrangement = analyzeArrangement;
|
|
889
|
-
//# sourceMappingURL=chunk-4VZ5MZAM.cjs.map
|