@libraz/libcantus 0.9.4 → 0.9.5
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 +27 -18
- package/README_ja.md +268 -0
- package/dist/analyze/index.cjs +7 -7
- package/dist/analyze/index.d.cts +9 -99
- package/dist/analyze/index.d.ts +9 -99
- package/dist/analyze/index.js +6 -6
- package/dist/{chunk-36N73KZ3.cjs → chunk-2A2QMA74.cjs} +80 -65
- package/dist/chunk-2A2QMA74.cjs.map +1 -0
- package/dist/{chunk-YLEMGEFH.js → chunk-2PTFGVD7.js} +50 -35
- package/dist/chunk-2PTFGVD7.js.map +1 -0
- package/dist/{chunk-ZEJR4TKL.cjs → chunk-357ZDPVU.cjs} +36 -47
- package/dist/chunk-357ZDPVU.cjs.map +1 -0
- package/dist/{chunk-EHVEBWM5.js → chunk-3KX76RLG.js} +136 -37
- package/dist/chunk-3KX76RLG.js.map +1 -0
- package/dist/{chunk-RC6GBMCD.cjs → chunk-4VZ5MZAM.cjs} +155 -111
- package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
- package/dist/{chunk-RK35RIN7.js → chunk-7EONYVI5.js} +115 -28
- package/dist/chunk-7EONYVI5.js.map +1 -0
- package/dist/{chunk-GQSGS2N4.js → chunk-7JEP3MFY.js} +5 -16
- package/dist/chunk-7JEP3MFY.js.map +1 -0
- package/dist/{chunk-7NKDPF74.js → chunk-AID75RN3.js} +203 -104
- package/dist/chunk-AID75RN3.js.map +1 -0
- package/dist/{chunk-FNDWXRRI.cjs → chunk-BSLTLGM3.cjs} +55 -37
- package/dist/chunk-BSLTLGM3.cjs.map +1 -0
- package/dist/{chunk-NP4NA2FB.cjs → chunk-CU6VG2YM.cjs} +39 -36
- package/dist/chunk-CU6VG2YM.cjs.map +1 -0
- package/dist/{chunk-NYYWMV2C.js → chunk-DZMHIGI4.js} +27 -17
- package/dist/chunk-DZMHIGI4.js.map +1 -0
- package/dist/{chunk-I24MV65U.js → chunk-EDOTEABK.js} +18 -15
- package/dist/{chunk-I24MV65U.js.map → chunk-EDOTEABK.js.map} +1 -1
- package/dist/{chunk-NKIBQ6PT.js → chunk-HN2OEUN2.js} +7 -4
- package/dist/chunk-HN2OEUN2.js.map +1 -0
- package/dist/{chunk-D2FAJBYL.js → chunk-KBTTEX7Q.js} +30 -12
- package/dist/chunk-KBTTEX7Q.js.map +1 -0
- package/dist/{chunk-CAN76NCU.cjs → chunk-KQN2ZYND.cjs} +147 -102
- package/dist/chunk-KQN2ZYND.cjs.map +1 -0
- package/dist/{chunk-4HLLZPXT.cjs → chunk-MPTVJIVG.cjs} +350 -251
- package/dist/chunk-MPTVJIVG.cjs.map +1 -0
- package/dist/{chunk-RUMU3UA6.cjs → chunk-MTMZTMWM.cjs} +12 -12
- package/dist/{chunk-RUMU3UA6.cjs.map → chunk-MTMZTMWM.cjs.map} +1 -1
- package/dist/{chunk-KMHOICIY.js → chunk-NNB67XA2.js} +90 -45
- package/dist/chunk-NNB67XA2.js.map +1 -0
- package/dist/{chunk-LW53AHQU.cjs → chunk-OL3IHHSJ.cjs} +228 -141
- package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
- package/dist/{chunk-7MGZ7D62.cjs → chunk-PI3UXRWK.cjs} +43 -13
- package/dist/chunk-PI3UXRWK.cjs.map +1 -0
- package/dist/{chunk-XEBG3G53.js → chunk-R2VVFAIN.js} +42 -12
- package/dist/chunk-R2VVFAIN.js.map +1 -0
- package/dist/{chunk-Q6OP4U43.cjs → chunk-WFDXOLUB.cjs} +50 -40
- package/dist/chunk-WFDXOLUB.cjs.map +1 -0
- package/dist/{chunk-NVE34YGZ.cjs → chunk-WTAN4ACG.cjs} +212 -113
- package/dist/chunk-WTAN4ACG.cjs.map +1 -0
- package/dist/{chunk-FCANXBAT.cjs → chunk-YFJXS3AU.cjs} +8 -5
- package/dist/chunk-YFJXS3AU.cjs.map +1 -0
- package/dist/{chunk-Q2XUP227.js → chunk-YUE6TJ4M.js} +89 -45
- package/dist/chunk-YUE6TJ4M.js.map +1 -0
- package/dist/{chunk-HKGYCQ34.js → chunk-ZGLJM4A4.js} +2 -2
- package/dist/core/index.cjs +15 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +14 -4
- package/dist/core/index.d.ts +14 -4
- package/dist/core/index.js +14 -4
- package/dist/{function-DNugAOrt.d.cts → function-CxixTB02.d.cts} +98 -7
- package/dist/{function-BtiZb9i9.d.ts → function-DTCt675z.d.ts} +98 -7
- package/dist/generate/index.cjs +10 -10
- package/dist/generate/index.d.cts +21 -21
- package/dist/generate/index.d.ts +21 -21
- package/dist/generate/index.js +9 -9
- package/dist/{index-tJhkmQgE.d.cts → index-B2Ulxxpo.d.cts} +3 -2
- package/dist/{index-Be74mcon.d.cts → index-BkYJ0xMq.d.cts} +2 -2
- package/dist/{index-6VR8-kit.d.ts → index-BuaB-ypt.d.ts} +3 -2
- package/dist/{index-70p168VM.d.ts → index-CAN7v-kw.d.ts} +9 -2
- package/dist/{index-C83mZI1P.d.ts → index-COw1Ky9g.d.ts} +1 -1
- package/dist/{index-tSQh-xwY.d.ts → index-Cfx8xgH0.d.ts} +1 -1
- package/dist/{index-C5UBbQ4G.d.ts → index-DRtIhlJj.d.ts} +2 -2
- package/dist/{index-BtXT6UQ0.d.cts → index-alrRninU.d.cts} +9 -2
- package/dist/{index-y_ak9_Hi.d.cts → index-oTQbYYIs.d.cts} +1 -1
- package/dist/{index-OG65ocOr.d.ts → index-tR9jTn26.d.cts} +4 -1
- package/dist/{index-OG65ocOr.d.cts → index-tR9jTn26.d.ts} +4 -1
- package/dist/{index-D6b6na69.d.cts → index-x2vcsduE.d.cts} +1 -1
- package/dist/index.cjs +24 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +23 -13
- package/dist/model/index.cjs +7 -7
- package/dist/model/index.d.cts +48 -16
- package/dist/model/index.d.ts +48 -16
- package/dist/model/index.js +6 -6
- package/dist/{styled-DIRkk9Uz.d.ts → styled-Bl0ZK28h.d.ts} +40 -3
- package/dist/{styled-DJbkJQfj.d.cts → styled-Crv6SA4S.d.cts} +40 -3
- package/dist/theory/index.cjs +6 -6
- package/dist/theory/index.d.cts +9 -9
- package/dist/theory/index.d.ts +9 -9
- package/dist/theory/index.js +5 -5
- package/package.json +12 -9
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- /package/dist/{chunk-HKGYCQ34.js.map → chunk-ZGLJM4A4.js.map} +0 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
@@ -24,7 +24,9 @@ var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
|
|
28
30
|
|
|
29
31
|
// src/theory/chordscale/index.ts
|
|
30
32
|
function popcount12(mask) {
|
|
@@ -35,7 +37,7 @@ function popcount12(mask) {
|
|
|
35
37
|
return count;
|
|
36
38
|
}
|
|
37
39
|
function maskHasPitchClass(mask, scaleRootPc, pc) {
|
|
38
|
-
const offset =
|
|
40
|
+
const offset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc - scaleRootPc);
|
|
39
41
|
return (mask >> offset & 1) === 1;
|
|
40
42
|
}
|
|
41
43
|
function scaleMatchesChord(chordPcs, scaleMask, scaleRootPc) {
|
|
@@ -59,46 +61,63 @@ var SCALE_PREFERENCE = [
|
|
|
59
61
|
"phrygian",
|
|
60
62
|
"locrian"
|
|
61
63
|
];
|
|
64
|
+
var IDIOMATIC_SCALES = {
|
|
65
|
+
aug: ["wholeTone"],
|
|
66
|
+
m7b5: ["locrian"]
|
|
67
|
+
};
|
|
62
68
|
function conventionalRank(name) {
|
|
63
69
|
const index = SCALE_PREFERENCE.indexOf(name);
|
|
64
70
|
return index < 0 ? SCALE_PREFERENCE.length : index;
|
|
65
71
|
}
|
|
72
|
+
function idiomaticRank(chord, name) {
|
|
73
|
+
const choices = IDIOMATIC_SCALES[chord.quality];
|
|
74
|
+
if (choices === void 0) {
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
const index = choices.indexOf(name);
|
|
78
|
+
return index < 0 ? choices.length + 1 : index;
|
|
79
|
+
}
|
|
80
|
+
function isAlteredDominant(chord, scaleName) {
|
|
81
|
+
return chord.quality === "dom7" && scaleName === "altered";
|
|
82
|
+
}
|
|
66
83
|
function chordScales(chord) {
|
|
67
|
-
const chordPcs =
|
|
68
|
-
const rootPc =
|
|
84
|
+
const chordPcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
|
|
85
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
69
86
|
const ranked = [];
|
|
70
87
|
const seenMasks = /* @__PURE__ */ new Set();
|
|
71
|
-
for (const name of Object.keys(
|
|
88
|
+
for (const name of Object.keys(_chunkBSLTLGM3cjs.NAMED_SCALES)) {
|
|
72
89
|
if (name === "chromatic" || ALIASED_SCALE_NAMES.has(name)) {
|
|
73
90
|
continue;
|
|
74
91
|
}
|
|
75
|
-
const mask =
|
|
92
|
+
const mask = _chunkBSLTLGM3cjs.namedScaleMask.call(void 0, name);
|
|
76
93
|
if (mask === void 0 || seenMasks.has(mask)) {
|
|
77
94
|
continue;
|
|
78
95
|
}
|
|
79
96
|
seenMasks.add(mask);
|
|
80
|
-
if (scaleMatchesChord(chordPcs, mask, rootPc)) {
|
|
97
|
+
if (scaleMatchesChord(chordPcs, mask, rootPc) || isAlteredDominant(chord, name)) {
|
|
81
98
|
const size = popcount12(mask);
|
|
82
99
|
ranked.push({ name, extra: size - chordPcs.length, size });
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
if (ranked.length === 0) {
|
|
86
|
-
if (
|
|
103
|
+
if (_chunkBSLTLGM3cjs.NAMED_SCALES.chromatic !== void 0) {
|
|
87
104
|
return [{ name: "chromatic", rootPc }];
|
|
88
105
|
}
|
|
89
106
|
return [];
|
|
90
107
|
}
|
|
91
108
|
const preferHeptatonic = chordPcs.length <= 3;
|
|
92
109
|
ranked.sort((a, b) => {
|
|
110
|
+
const idiomaticA = idiomaticRank(chord, a.name);
|
|
111
|
+
const idiomaticB = idiomaticRank(chord, b.name);
|
|
112
|
+
if (idiomaticA !== idiomaticB) {
|
|
113
|
+
return idiomaticA - idiomaticB;
|
|
114
|
+
}
|
|
93
115
|
if (preferHeptatonic && a.size === HEPTATONIC_SIZE !== (b.size === HEPTATONIC_SIZE)) {
|
|
94
116
|
return a.size === HEPTATONIC_SIZE ? -1 : 1;
|
|
95
117
|
}
|
|
96
118
|
if (a.extra !== b.extra) {
|
|
97
119
|
return a.extra - b.extra;
|
|
98
120
|
}
|
|
99
|
-
if (a.size !== b.size) {
|
|
100
|
-
return a.size - b.size;
|
|
101
|
-
}
|
|
102
121
|
const rankA = conventionalRank(a.name);
|
|
103
122
|
const rankB = conventionalRank(b.name);
|
|
104
123
|
if (rankA !== rankB) {
|
|
@@ -109,10 +128,11 @@ function chordScales(chord) {
|
|
|
109
128
|
return ranked.map((entry) => ({ name: entry.name, rootPc }));
|
|
110
129
|
}
|
|
111
130
|
function avoidNotes(chord, scaleName) {
|
|
112
|
-
const mask =
|
|
113
|
-
const rootPc =
|
|
114
|
-
const chordPcs =
|
|
115
|
-
|
|
131
|
+
const mask = _chunkBSLTLGM3cjs.requireScaleMask.call(void 0, scaleName);
|
|
132
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
133
|
+
const chordPcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
|
|
134
|
+
const alteredDominant = isAlteredDominant(chord, scaleName);
|
|
135
|
+
if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
|
|
116
136
|
return [];
|
|
117
137
|
}
|
|
118
138
|
const chordSet = new Set(chordPcs);
|
|
@@ -121,21 +141,23 @@ function avoidNotes(chord, scaleName) {
|
|
|
121
141
|
if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
|
|
122
142
|
continue;
|
|
123
143
|
}
|
|
124
|
-
|
|
144
|
+
const suspendedThird = chord.quality === "sus4" || chord.quality === "7sus4" ? pc === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc + 4) : false;
|
|
145
|
+
if (!alteredDominant && (chordSet.has(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc - 1)) || suspendedThird)) {
|
|
125
146
|
avoid.push(pc);
|
|
126
147
|
}
|
|
127
148
|
}
|
|
128
149
|
return avoid.sort((a, b) => a - b);
|
|
129
150
|
}
|
|
130
151
|
function availableTensions(chord, scaleName) {
|
|
131
|
-
const mask =
|
|
132
|
-
const rootPc =
|
|
133
|
-
const chordPcs =
|
|
134
|
-
|
|
152
|
+
const mask = _chunkBSLTLGM3cjs.requireScaleMask.call(void 0, scaleName);
|
|
153
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
154
|
+
const chordPcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
|
|
155
|
+
const alteredDominant = isAlteredDominant(chord, scaleName);
|
|
156
|
+
if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
|
|
135
157
|
return [];
|
|
136
158
|
}
|
|
137
159
|
const chordSet = new Set(chordPcs);
|
|
138
|
-
const avoidSet = new Set(avoidNotes(chord, scaleName));
|
|
160
|
+
const avoidSet = alteredDominant ? /* @__PURE__ */ new Set() : new Set(avoidNotes(chord, scaleName));
|
|
139
161
|
const tensions = [];
|
|
140
162
|
for (let pc = 0; pc < 12; pc += 1) {
|
|
141
163
|
if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc) || avoidSet.has(pc)) {
|
|
@@ -146,8 +168,11 @@ function availableTensions(chord, scaleName) {
|
|
|
146
168
|
return tensions.sort((a, b) => a - b);
|
|
147
169
|
}
|
|
148
170
|
function chordScaleReport(chord, limit) {
|
|
171
|
+
if (limit !== void 0) {
|
|
172
|
+
_chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, limit, "chord-scale report limit");
|
|
173
|
+
}
|
|
149
174
|
const matches = chordScales(chord);
|
|
150
|
-
const chosen = limit === void 0 ? matches : matches.slice(0,
|
|
175
|
+
const chosen = limit === void 0 ? matches : matches.slice(0, limit);
|
|
151
176
|
return chosen.map((match) => ({
|
|
152
177
|
name: match.name,
|
|
153
178
|
rootPc: match.rootPc,
|
|
@@ -157,14 +182,14 @@ function chordScaleReport(chord, limit) {
|
|
|
157
182
|
}
|
|
158
183
|
var RANK_PENALTY = 0.01;
|
|
159
184
|
function scalePitchClassSet(name, rootPc) {
|
|
160
|
-
const mask =
|
|
185
|
+
const mask = _chunkBSLTLGM3cjs.namedScaleMask.call(void 0, name);
|
|
161
186
|
const pcs = /* @__PURE__ */ new Set();
|
|
162
187
|
if (mask === void 0) {
|
|
163
188
|
return pcs;
|
|
164
189
|
}
|
|
165
190
|
for (let n = 0; n < 12; n += 1) {
|
|
166
191
|
if ((mask >> n & 1) === 1) {
|
|
167
|
-
pcs.add(
|
|
192
|
+
pcs.add(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc + n));
|
|
168
193
|
}
|
|
169
194
|
}
|
|
170
195
|
return pcs;
|
|
@@ -184,6 +209,7 @@ function symmetricDifferenceSize(a, b) {
|
|
|
184
209
|
return count;
|
|
185
210
|
}
|
|
186
211
|
function scalesForChanges(chords) {
|
|
212
|
+
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, chords.length, "chord-scale changes");
|
|
187
213
|
if (chords.length === 0) {
|
|
188
214
|
return [];
|
|
189
215
|
}
|
|
@@ -296,7 +322,7 @@ var CHROMATIC_SPELLING_SHARP = {
|
|
|
296
322
|
// M7
|
|
297
323
|
};
|
|
298
324
|
function alterFor(letter, pc) {
|
|
299
|
-
let d =
|
|
325
|
+
let d = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc - _chunkPI3UXRWKcjs.naturalPitchClassOf.call(void 0, letter));
|
|
300
326
|
if (d > 6) {
|
|
301
327
|
d -= 12;
|
|
302
328
|
}
|
|
@@ -306,7 +332,7 @@ function letterFor(tonic, degreeOffset, pc) {
|
|
|
306
332
|
if (degreeOffset === void 0) {
|
|
307
333
|
return void 0;
|
|
308
334
|
}
|
|
309
|
-
const letter =
|
|
335
|
+
const letter = _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, tonic.letter + degreeOffset);
|
|
310
336
|
return { letter, alter: alterFor(letter, pc) };
|
|
311
337
|
}
|
|
312
338
|
function pickSpelling(flat, sharp, preferFlat) {
|
|
@@ -318,31 +344,37 @@ function pickSpelling(flat, sharp, preferFlat) {
|
|
|
318
344
|
}
|
|
319
345
|
return preferFlat ? flat : sharp;
|
|
320
346
|
}
|
|
347
|
+
function capChromaticAccidentals(preferred, alternative) {
|
|
348
|
+
return alternative !== void 0 && Math.abs(preferred.alter) - Math.abs(alternative.alter) >= 2 ? alternative : preferred;
|
|
349
|
+
}
|
|
321
350
|
function isHeptatonic(key) {
|
|
322
|
-
return
|
|
351
|
+
return _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).length === 7;
|
|
323
352
|
}
|
|
324
353
|
function hasMinorThird(key) {
|
|
325
|
-
const tones =
|
|
326
|
-
const root =
|
|
327
|
-
return tones.includes(
|
|
354
|
+
const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key);
|
|
355
|
+
const root = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
356
|
+
return tones.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, root + 3)) && !tones.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, root + 4));
|
|
328
357
|
}
|
|
329
358
|
function spellPitchClass(pc, tonic, key) {
|
|
330
|
-
const tonicPc =
|
|
331
|
-
const offset =
|
|
359
|
+
const tonicPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _chunkPI3UXRWKcjs.naturalPitchClassOf.call(void 0, tonic.letter) + tonic.alter);
|
|
360
|
+
const offset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc - tonicPc);
|
|
332
361
|
if (offset === 0) {
|
|
333
|
-
return { letter:
|
|
362
|
+
return { letter: _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, tonic.letter), alter: tonic.alter };
|
|
334
363
|
}
|
|
335
364
|
const heptatonic = isHeptatonic(key);
|
|
336
365
|
if (heptatonic) {
|
|
337
|
-
const degree =
|
|
366
|
+
const degree = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).indexOf(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc));
|
|
338
367
|
if (degree >= 0) {
|
|
339
|
-
const letter =
|
|
368
|
+
const letter = _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, tonic.letter + degree);
|
|
340
369
|
return { letter, alter: alterFor(letter, pc) };
|
|
341
370
|
}
|
|
342
371
|
const degreeOffset = CHROMATIC_SPELLING[offset];
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
372
|
+
const preferred = letterFor(tonic, degreeOffset, pc);
|
|
373
|
+
if (preferred !== void 0) {
|
|
374
|
+
const flat = letterFor(tonic, CHROMATIC_SPELLING_FLAT[offset], pc);
|
|
375
|
+
const sharp = letterFor(tonic, CHROMATIC_SPELLING_SHARP[offset], pc);
|
|
376
|
+
const alternative = _optionalChain([flat, 'optionalAccess', _4 => _4.letter]) === preferred.letter ? sharp : _optionalChain([sharp, 'optionalAccess', _5 => _5.letter]) === preferred.letter ? flat : void 0;
|
|
377
|
+
return capChromaticAccidentals(preferred, alternative);
|
|
346
378
|
}
|
|
347
379
|
} else {
|
|
348
380
|
const flat = letterFor(tonic, CHROMATIC_SPELLING_FLAT[offset], pc);
|
|
@@ -352,11 +384,11 @@ function spellPitchClass(pc, tonic, key) {
|
|
|
352
384
|
return best;
|
|
353
385
|
}
|
|
354
386
|
}
|
|
355
|
-
const belowLetter =
|
|
387
|
+
const belowLetter = _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, _nullishCoalesce([0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6][_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc)], () => ( 0)));
|
|
356
388
|
return { letter: belowLetter, alter: alterFor(belowLetter, pc) };
|
|
357
389
|
}
|
|
358
390
|
function spellScale(tonic, key) {
|
|
359
|
-
return
|
|
391
|
+
return _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => spellPitchClass(pc, tonic, key));
|
|
360
392
|
}
|
|
361
393
|
function spellPitchClasses(pcs, tonic, key) {
|
|
362
394
|
return pcs.map((pc) => spellPitchClass(pc, tonic, key));
|
|
@@ -398,16 +430,19 @@ function chordLetterOffset(interval, chord) {
|
|
|
398
430
|
return 6;
|
|
399
431
|
default: {
|
|
400
432
|
const octaves = Math.floor(Math.max(0, interval) / 12);
|
|
401
|
-
const simple =
|
|
433
|
+
const simple = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, interval);
|
|
402
434
|
const simpleOffset = _nullishCoalesce([0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6][simple], () => ( 0));
|
|
403
435
|
return octaves * 7 + simpleOffset;
|
|
404
436
|
}
|
|
405
437
|
}
|
|
406
438
|
}
|
|
407
439
|
function spellChordFromRoot(chord, root) {
|
|
440
|
+
if (_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _chunkPI3UXRWKcjs.naturalPitchClassOf.call(void 0, root.letter) + root.alter) !== _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc)) {
|
|
441
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("chord root spelling must match chord.rootPc");
|
|
442
|
+
}
|
|
408
443
|
return chord.intervals.map((interval) => {
|
|
409
|
-
const letter =
|
|
410
|
-
const pc =
|
|
444
|
+
const letter = _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, root.letter + chordLetterOffset(interval, chord));
|
|
445
|
+
const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
|
|
411
446
|
return { letter, alter: alterFor(letter, pc) };
|
|
412
447
|
});
|
|
413
448
|
}
|
|
@@ -416,26 +451,27 @@ function spellChord(chord, tonic, key) {
|
|
|
416
451
|
}
|
|
417
452
|
function chordRootSpelling(chord, tonic, key) {
|
|
418
453
|
const hint = chord.rootSpelling;
|
|
419
|
-
if (hint !== void 0 &&
|
|
420
|
-
return { letter:
|
|
454
|
+
if (hint !== void 0 && _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _chunkPI3UXRWKcjs.naturalPitchClassOf.call(void 0, hint.letter) + hint.alter) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc)) {
|
|
455
|
+
return { letter: _chunkPI3UXRWKcjs.diatonicLetterOf.call(void 0, hint.letter), alter: hint.alter };
|
|
421
456
|
}
|
|
422
457
|
return spellPitchClass(chord.rootPc, tonic, key);
|
|
423
458
|
}
|
|
424
459
|
function spellPitch(pitch, tonic, key) {
|
|
425
460
|
const rounded = Math.round(pitch);
|
|
426
|
-
const spelled = spellPitchClass(
|
|
427
|
-
const octave = (rounded -
|
|
461
|
+
const spelled = spellPitchClass(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rounded), tonic, key);
|
|
462
|
+
const octave = (rounded - _chunkPI3UXRWKcjs.naturalPitchClassOf.call(void 0, spelled.letter) - spelled.alter) / 12 - 1;
|
|
428
463
|
return { letter: spelled.letter, alter: spelled.alter, octave };
|
|
429
464
|
}
|
|
430
465
|
function noteNames(notes) {
|
|
431
|
-
return notes.map(
|
|
466
|
+
return notes.map(_chunkPI3UXRWKcjs.formatNote);
|
|
432
467
|
}
|
|
433
468
|
|
|
434
469
|
// src/theory/symbol/index.ts
|
|
435
|
-
var ROOT_RE = /^[A-Ga-g](?:#{1,2}|b{1,2})?/;
|
|
436
|
-
var BASS_RE = /^[A-Ga-g](?:#{1,2}|b{1,2})?$/;
|
|
470
|
+
var ROOT_RE = /^[A-Ga-g](?:#{1,2}|b{1,2}|x)?/;
|
|
471
|
+
var BASS_RE = /^[A-Ga-g](?:#{1,2}|b{1,2}|x)?$/;
|
|
437
472
|
var QUALITY_MAP = {
|
|
438
473
|
"": "maj",
|
|
474
|
+
maj: "maj",
|
|
439
475
|
m: "min",
|
|
440
476
|
min: "min",
|
|
441
477
|
"-": "min",
|
|
@@ -460,6 +496,15 @@ var QUALITY_MAP = {
|
|
|
460
496
|
mMaj7: "minMaj7",
|
|
461
497
|
mM7: "minMaj7",
|
|
462
498
|
minMaj7: "minMaj7",
|
|
499
|
+
mMaj9: "minMaj9",
|
|
500
|
+
mM9: "minMaj9",
|
|
501
|
+
minMaj9: "minMaj9",
|
|
502
|
+
mMaj11: "minMaj11",
|
|
503
|
+
mM11: "minMaj11",
|
|
504
|
+
minMaj11: "minMaj11",
|
|
505
|
+
mMaj13: "minMaj13",
|
|
506
|
+
mM13: "minMaj13",
|
|
507
|
+
minMaj13: "minMaj13",
|
|
463
508
|
aug7: "aug7",
|
|
464
509
|
"+7": "aug7",
|
|
465
510
|
"7#5": "aug7",
|
|
@@ -533,6 +578,9 @@ var CANONICAL_SUFFIX = {
|
|
|
533
578
|
dim7: "dim7",
|
|
534
579
|
m7b5: "m7b5",
|
|
535
580
|
minMaj7: "mMaj7",
|
|
581
|
+
minMaj9: "mMaj9",
|
|
582
|
+
minMaj11: "mMaj11",
|
|
583
|
+
minMaj13: "mMaj13",
|
|
536
584
|
aug7: "aug7",
|
|
537
585
|
augMaj7: "augMaj7",
|
|
538
586
|
majb5: "maj(b5)",
|
|
@@ -568,10 +616,10 @@ function knownQuality(suffix) {
|
|
|
568
616
|
return Object.hasOwn(QUALITY_MAP, suffix) ? QUALITY_MAP[suffix] : void 0;
|
|
569
617
|
}
|
|
570
618
|
function makeSpelledChord(root, quality, bass) {
|
|
571
|
-
const chord =
|
|
572
|
-
|
|
619
|
+
const chord = _chunkBSLTLGM3cjs.makeChord.call(void 0,
|
|
620
|
+
_chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, root),
|
|
573
621
|
quality,
|
|
574
|
-
bass === void 0 ? void 0 :
|
|
622
|
+
bass === void 0 ? void 0 : _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, bass)
|
|
575
623
|
);
|
|
576
624
|
chord.rootSpelling = { letter: root.letter, alter: root.alter };
|
|
577
625
|
if (bass !== void 0) {
|
|
@@ -580,13 +628,13 @@ function makeSpelledChord(root, quality, bass) {
|
|
|
580
628
|
return chord;
|
|
581
629
|
}
|
|
582
630
|
function parseChordSymbol(text) {
|
|
583
|
-
const trimmed = text.trim();
|
|
631
|
+
const trimmed = text.trim().replaceAll("\u266F", "#").replaceAll("\u266D", "b").replaceAll("\u{1D12A}", "x").replaceAll("\u{1D12B}", "bb");
|
|
584
632
|
const rootMatch = ROOT_RE.exec(trimmed);
|
|
585
633
|
if (!rootMatch) {
|
|
586
|
-
throw new (0,
|
|
634
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Invalid chord symbol: ${text}`);
|
|
587
635
|
}
|
|
588
636
|
const rootToken = rootMatch[0];
|
|
589
|
-
const rootNote =
|
|
637
|
+
const rootNote = _chunkPI3UXRWKcjs.parseNote.call(void 0, rootToken);
|
|
590
638
|
const rest = trimmed.slice(rootToken.length);
|
|
591
639
|
const slashIndex = rest.lastIndexOf("/");
|
|
592
640
|
if (slashIndex !== -1) {
|
|
@@ -594,46 +642,55 @@ function parseChordSymbol(text) {
|
|
|
594
642
|
const after = rest.slice(slashIndex + 1);
|
|
595
643
|
const beforeQuality = knownQuality(before);
|
|
596
644
|
if (beforeQuality !== void 0 && BASS_RE.test(after)) {
|
|
597
|
-
return makeSpelledChord(rootNote, beforeQuality,
|
|
645
|
+
return makeSpelledChord(rootNote, beforeQuality, _chunkPI3UXRWKcjs.parseNote.call(void 0, after));
|
|
598
646
|
}
|
|
599
647
|
}
|
|
600
648
|
const quality = knownQuality(rest);
|
|
601
649
|
if (quality === void 0) {
|
|
602
|
-
throw new (0,
|
|
650
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Unrecognized chord quality: ${text}`);
|
|
603
651
|
}
|
|
604
652
|
return makeSpelledChord(rootNote, quality);
|
|
605
653
|
}
|
|
606
654
|
function pitchClassName(pc, hint, flats, inheritFlats) {
|
|
607
|
-
if (flats === void 0 && hint !== void 0 &&
|
|
608
|
-
return
|
|
655
|
+
if (flats === void 0 && hint !== void 0 && _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, hint) === pc) {
|
|
656
|
+
return _chunkPI3UXRWKcjs.formatNote.call(void 0, hint);
|
|
609
657
|
}
|
|
610
|
-
const note =
|
|
611
|
-
return
|
|
658
|
+
const note = _chunkPI3UXRWKcjs.midiToNote.call(void 0, 60 + pc, _nullishCoalesce(flats, () => ( inheritFlats)) ? "flat" : "sharp");
|
|
659
|
+
return _chunkPI3UXRWKcjs.formatNote.call(void 0, { letter: note.letter, alter: note.alter });
|
|
612
660
|
}
|
|
613
661
|
function formatChordSymbol(chord, opts) {
|
|
614
|
-
|
|
662
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, chord.rootPc, "chord rootPc");
|
|
615
663
|
if (chord.bassPc !== void 0) {
|
|
616
|
-
|
|
664
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, chord.bassPc, "chord bassPc");
|
|
617
665
|
}
|
|
618
666
|
if (!Object.hasOwn(CANONICAL_SUFFIX, chord.quality)) {
|
|
619
|
-
throw new (0,
|
|
667
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Unknown chord quality: ${String(chord.quality)}`);
|
|
620
668
|
}
|
|
621
|
-
const rootPc =
|
|
669
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
622
670
|
const rootHint = chord.rootSpelling;
|
|
623
|
-
const rootName = pitchClassName(rootPc, rootHint, _optionalChain([opts, 'optionalAccess',
|
|
671
|
+
const rootName = pitchClassName(rootPc, rootHint, _optionalChain([opts, 'optionalAccess', _6 => _6.flats]));
|
|
624
672
|
const suffix = CANONICAL_SUFFIX[chord.quality];
|
|
625
673
|
let symbol = `${rootName}${suffix}`;
|
|
626
|
-
if (chord.bassPc !== void 0 &&
|
|
627
|
-
const inheritFlats = rootHint !== void 0 &&
|
|
628
|
-
symbol += `/${pitchClassName(
|
|
674
|
+
if (chord.bassPc !== void 0 && _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.bassPc) !== rootPc) {
|
|
675
|
+
const inheritFlats = rootHint !== void 0 && _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, rootHint) === rootPc ? rootHint.alter < 0 : void 0;
|
|
676
|
+
symbol += `/${pitchClassName(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.bassPc), chord.bassSpelling, _optionalChain([opts, 'optionalAccess', _7 => _7.flats]), inheritFlats)}`;
|
|
629
677
|
}
|
|
630
678
|
return symbol;
|
|
631
679
|
}
|
|
632
680
|
function transposeChordSymbol(text, semitones, opts) {
|
|
681
|
+
_chunkPI3UXRWKcjs.assertFiniteSemitones.call(void 0, semitones);
|
|
633
682
|
const chord = parseChordSymbol(text);
|
|
634
|
-
const transposed = { ...chord, rootPc:
|
|
683
|
+
const transposed = { ...chord, rootPc: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + semitones) };
|
|
684
|
+
if (chord.rootSpelling !== void 0) {
|
|
685
|
+
const hint = _chunkPI3UXRWKcjs.transposeNote.call(void 0, chord.rootSpelling, semitones);
|
|
686
|
+
transposed.rootSpelling = { letter: hint.letter, alter: hint.alter };
|
|
687
|
+
}
|
|
635
688
|
if (chord.bassPc !== void 0) {
|
|
636
|
-
transposed.bassPc =
|
|
689
|
+
transposed.bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.bassPc + semitones);
|
|
690
|
+
}
|
|
691
|
+
if (chord.bassSpelling !== void 0) {
|
|
692
|
+
const hint = _chunkPI3UXRWKcjs.transposeNote.call(void 0, chord.bassSpelling, semitones);
|
|
693
|
+
transposed.bassSpelling = { letter: hint.letter, alter: hint.alter };
|
|
637
694
|
}
|
|
638
695
|
return formatChordSymbol(transposed, opts);
|
|
639
696
|
}
|
|
@@ -644,14 +701,15 @@ var VIOLATION_PENALTY = 1e3;
|
|
|
644
701
|
var MISSING_TONE_PENALTY = 500;
|
|
645
702
|
var MISSING_FIFTH_PENALTY = 20;
|
|
646
703
|
var POOR_DOUBLING_PENALTY = 4;
|
|
704
|
+
var UNISON_PENALTY = 100;
|
|
647
705
|
var LEADING_TONE_DOUBLING_PENALTY = 200;
|
|
648
|
-
var RESOLUTION_PENALTY =
|
|
649
|
-
var
|
|
650
|
-
var
|
|
706
|
+
var RESOLUTION_PENALTY = 200;
|
|
707
|
+
var DEFAULT_MAX_CANDIDATES = 4e3;
|
|
708
|
+
var SEARCH_NODES_PER_CANDIDATE = 16;
|
|
651
709
|
function pitchesForPc(pc, range) {
|
|
652
710
|
const result = [];
|
|
653
711
|
for (let midi = Math.ceil(range.min); midi <= range.max; midi += 1) {
|
|
654
|
-
if (
|
|
712
|
+
if (_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, midi) === pc) {
|
|
655
713
|
result.push(midi);
|
|
656
714
|
}
|
|
657
715
|
}
|
|
@@ -662,23 +720,24 @@ function pitchesForPc(pc, range) {
|
|
|
662
720
|
return da === db ? a - b : da - db;
|
|
663
721
|
});
|
|
664
722
|
}
|
|
665
|
-
function enumerateVoicings(chord, ranges, maxSpacing) {
|
|
666
|
-
const chordPcs =
|
|
667
|
-
const bassPc =
|
|
723
|
+
function enumerateVoicings(chord, ranges, maxSpacing, maxCandidates = DEFAULT_MAX_CANDIDATES) {
|
|
724
|
+
const chordPcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
|
|
725
|
+
const bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
|
|
668
726
|
const byVoice = ranges.map(
|
|
669
727
|
(range, voice) => (voice === 0 ? [bassPc] : chordPcs).flatMap((pc) => pitchesForPc(pc, range))
|
|
670
728
|
);
|
|
671
729
|
for (let voice = 0; voice < byVoice.length; voice += 1) {
|
|
672
730
|
if ((_nullishCoalesce(byVoice[voice], () => ( []))).length === 0) {
|
|
673
|
-
throw new (0,
|
|
731
|
+
throw new (0, _chunkPI3UXRWKcjs.NoSolutionError)("no voicing satisfies the given ranges");
|
|
674
732
|
}
|
|
675
733
|
}
|
|
676
734
|
const results = [];
|
|
677
735
|
const current = [];
|
|
678
736
|
let nodes = 0;
|
|
737
|
+
const maxSearchNodes = maxCandidates * SEARCH_NODES_PER_CANDIDATE;
|
|
679
738
|
const build = (voice) => {
|
|
680
739
|
nodes += 1;
|
|
681
|
-
if (results.length >=
|
|
740
|
+
if (results.length >= maxCandidates || nodes > maxSearchNodes) {
|
|
682
741
|
return;
|
|
683
742
|
}
|
|
684
743
|
if (voice === ranges.length) {
|
|
@@ -707,17 +766,22 @@ function enumerateVoicings(chord, ranges, maxSpacing) {
|
|
|
707
766
|
function structuralPenalty(pitches, chord, key) {
|
|
708
767
|
const counts = /* @__PURE__ */ new Map();
|
|
709
768
|
for (const pitch of pitches) {
|
|
710
|
-
const pc =
|
|
769
|
+
const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch);
|
|
711
770
|
counts.set(pc, (_nullishCoalesce(counts.get(pc), () => ( 0))) + 1);
|
|
712
771
|
}
|
|
713
772
|
let penalty = 0;
|
|
714
|
-
for (
|
|
773
|
+
for (let index = 1; index < pitches.length; index += 1) {
|
|
774
|
+
if (pitches[index] === pitches[index - 1]) {
|
|
775
|
+
penalty += UNISON_PENALTY;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
for (const pc of _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord)) {
|
|
715
779
|
if (!counts.has(pc)) {
|
|
716
|
-
penalty +=
|
|
780
|
+
penalty += _chunkBSLTLGM3cjs.chordToneRole.call(void 0, pc, chord) === "fifth" ? MISSING_FIFTH_PENALTY : MISSING_TONE_PENALTY;
|
|
717
781
|
}
|
|
718
782
|
}
|
|
719
|
-
const rootPc =
|
|
720
|
-
const leadingTonePc = key === void 0 ? void 0 :
|
|
783
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
784
|
+
const leadingTonePc = key === void 0 ? void 0 : _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc - 1);
|
|
721
785
|
for (const [pc, count] of counts) {
|
|
722
786
|
if (count <= 1) {
|
|
723
787
|
continue;
|
|
@@ -726,7 +790,7 @@ function structuralPenalty(pitches, chord, key) {
|
|
|
726
790
|
penalty += (count - 1) * LEADING_TONE_DOUBLING_PENALTY;
|
|
727
791
|
continue;
|
|
728
792
|
}
|
|
729
|
-
if (pc !== rootPc &&
|
|
793
|
+
if (pc !== rootPc && _chunkBSLTLGM3cjs.chordToneRole.call(void 0, pc, chord) !== "fifth") {
|
|
730
794
|
penalty += (count - 1) * POOR_DOUBLING_PENALTY;
|
|
731
795
|
}
|
|
732
796
|
}
|
|
@@ -734,8 +798,8 @@ function structuralPenalty(pitches, chord, key) {
|
|
|
734
798
|
}
|
|
735
799
|
function seventhPcOf(chord) {
|
|
736
800
|
for (const interval of chord.intervals) {
|
|
737
|
-
const pc =
|
|
738
|
-
if (
|
|
801
|
+
const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
|
|
802
|
+
if (_chunkBSLTLGM3cjs.chordToneRole.call(void 0, pc, chord) === "seventh") {
|
|
739
803
|
return pc;
|
|
740
804
|
}
|
|
741
805
|
}
|
|
@@ -743,9 +807,9 @@ function seventhPcOf(chord) {
|
|
|
743
807
|
}
|
|
744
808
|
function resolutionViolations(prev, cur, prevChord, nextChord, key) {
|
|
745
809
|
const seventhPc = seventhPcOf(prevChord);
|
|
746
|
-
const leadingTonePc = key === void 0 ? void 0 :
|
|
747
|
-
const tonicPc = key === void 0 ? void 0 :
|
|
748
|
-
const nextPcs = new Set(
|
|
810
|
+
const leadingTonePc = key === void 0 ? void 0 : _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc - 1);
|
|
811
|
+
const tonicPc = key === void 0 ? void 0 : _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
|
|
812
|
+
const nextPcs = new Set(_chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, nextChord));
|
|
749
813
|
let count = 0;
|
|
750
814
|
for (let voice = 0; voice < prev.length && voice < cur.length; voice += 1) {
|
|
751
815
|
const from = prev[voice];
|
|
@@ -753,7 +817,7 @@ function resolutionViolations(prev, cur, prevChord, nextChord, key) {
|
|
|
753
817
|
if (from === void 0 || to === void 0) {
|
|
754
818
|
continue;
|
|
755
819
|
}
|
|
756
|
-
const fromPc =
|
|
820
|
+
const fromPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, from);
|
|
757
821
|
const motion = to - from;
|
|
758
822
|
if (fromPc === seventhPc && !nextPcs.has(fromPc) && motion !== -1 && motion !== -2) {
|
|
759
823
|
count += 1;
|
|
@@ -775,11 +839,11 @@ function violationCount(prev, cur) {
|
|
|
775
839
|
if (prevLower === void 0 || prevUpper === void 0 || curLower === void 0 || curUpper === void 0) {
|
|
776
840
|
continue;
|
|
777
841
|
}
|
|
778
|
-
if (
|
|
842
|
+
if (_chunkBSLTLGM3cjs.createsParallelPerfect.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
|
|
779
843
|
count += 1;
|
|
780
844
|
}
|
|
781
845
|
if (upper === lower + 1) {
|
|
782
|
-
if (
|
|
846
|
+
if (_chunkBSLTLGM3cjs.createsVoiceOverlap.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
|
|
783
847
|
count += 1;
|
|
784
848
|
}
|
|
785
849
|
}
|
|
@@ -808,39 +872,52 @@ function voiceLeadingCost(from, to) {
|
|
|
808
872
|
const bassCur = to[0];
|
|
809
873
|
const sopPrev = from[from.length - 1];
|
|
810
874
|
const sopCur = to[to.length - 1];
|
|
811
|
-
if (bassPrev !== void 0 && bassCur !== void 0 && sopPrev !== void 0 && sopCur !== void 0 &&
|
|
875
|
+
if (bassPrev !== void 0 && bassCur !== void 0 && sopPrev !== void 0 && sopCur !== void 0 && _chunkBSLTLGM3cjs.createsHiddenParallelPerfect.call(void 0, bassPrev, bassCur, sopPrev, sopCur)) {
|
|
812
876
|
total += HIDDEN_PERFECT_PENALTY;
|
|
813
877
|
}
|
|
814
878
|
}
|
|
815
879
|
return total;
|
|
816
880
|
}
|
|
817
881
|
function nextVoicing(current, chord, opts) {
|
|
818
|
-
const derived = _optionalChain([opts, 'optionalAccess',
|
|
882
|
+
const derived = _optionalChain([opts, 'optionalAccess', _8 => _8.ranges]) === void 0 && _optionalChain([opts, 'optionalAccess', _9 => _9.voices]) === void 0;
|
|
819
883
|
if (derived) {
|
|
820
884
|
if (current.length === 0) {
|
|
821
|
-
throw new (0,
|
|
885
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("current must contain at least one pitch");
|
|
822
886
|
}
|
|
823
887
|
for (let index = 0; index < current.length; index += 1) {
|
|
824
|
-
|
|
888
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
|
|
825
889
|
}
|
|
826
890
|
}
|
|
827
891
|
const ranges = derived ? current.map((pitch) => {
|
|
828
892
|
const centre = Math.min(127, Math.max(0, pitch));
|
|
829
893
|
return { min: Math.max(0, centre - 12), max: Math.min(127, centre + 12) };
|
|
830
894
|
}) : resolveRanges(opts);
|
|
895
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
896
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
|
|
897
|
+
}
|
|
898
|
+
if (current.length < ranges.length) {
|
|
899
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
|
|
900
|
+
`current has ${current.length} voices but the requested ranges require ${ranges.length}`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
const source = current.length === ranges.length ? current : ranges.length === 1 ? [_nullishCoalesce(current[0], () => ( Number.NaN))] : ranges.map((_, index) => {
|
|
904
|
+
const sourceIndex = Math.round(index * (current.length - 1) / (ranges.length - 1));
|
|
905
|
+
return _nullishCoalesce(current[sourceIndex], () => ( Number.NaN));
|
|
906
|
+
});
|
|
831
907
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
832
|
-
const candidates = enumerateVoicings(chord, ranges, maxSpacing);
|
|
908
|
+
const candidates = enumerateVoicings(chord, ranges, maxSpacing, resolveMaxCandidates(opts));
|
|
909
|
+
const previousChord = _optionalChain([opts, 'optionalAccess', _10 => _10.previousChord]);
|
|
833
910
|
let best;
|
|
834
911
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
835
912
|
for (const candidate of candidates) {
|
|
836
|
-
const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess',
|
|
913
|
+
const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _11 => _11.key])) + voiceLeadingCost(source, candidate) + VIOLATION_PENALTY * violationCount(source, candidate) + RESOLUTION_PENALTY * (previousChord === void 0 ? 0 : resolutionViolations(source, candidate, previousChord, chord, _optionalChain([opts, 'optionalAccess', _12 => _12.key])));
|
|
837
914
|
if (score < bestScore) {
|
|
838
915
|
bestScore = score;
|
|
839
916
|
best = candidate;
|
|
840
917
|
}
|
|
841
918
|
}
|
|
842
919
|
if (best === void 0) {
|
|
843
|
-
throw new (0,
|
|
920
|
+
throw new (0, _chunkPI3UXRWKcjs.NoSolutionError)("no voicing satisfies the given ranges");
|
|
844
921
|
}
|
|
845
922
|
return best;
|
|
846
923
|
}
|
|
@@ -856,18 +933,18 @@ var DERIVED_LOW = 40;
|
|
|
856
933
|
var DERIVED_HIGH = 79;
|
|
857
934
|
var DERIVED_SPAN = 19;
|
|
858
935
|
function resolveRanges(opts) {
|
|
859
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
936
|
+
if (_optionalChain([opts, 'optionalAccess', _13 => _13.ranges]) !== void 0) {
|
|
860
937
|
if (opts.ranges.length === 0) {
|
|
861
|
-
throw new (0,
|
|
938
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("ranges must contain at least one voice range");
|
|
862
939
|
}
|
|
863
|
-
|
|
940
|
+
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, opts.ranges.length, "voice ranges", 128);
|
|
864
941
|
return opts.ranges.map((range, index) => {
|
|
865
|
-
|
|
866
|
-
|
|
942
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, range.min, `ranges[${index}].min`);
|
|
943
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, range.max, `ranges[${index}].max`);
|
|
867
944
|
if (range.min > range.max) {
|
|
868
|
-
throw new (0,
|
|
945
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`ranges[${index}].min must not exceed max`);
|
|
869
946
|
}
|
|
870
|
-
|
|
947
|
+
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0,
|
|
871
948
|
Math.floor(range.max) - Math.ceil(range.min) + 1,
|
|
872
949
|
`ranges[${index}] span`,
|
|
873
950
|
4096
|
|
@@ -875,8 +952,8 @@ function resolveRanges(opts) {
|
|
|
875
952
|
return { ...range };
|
|
876
953
|
});
|
|
877
954
|
}
|
|
878
|
-
const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
879
|
-
|
|
955
|
+
const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _14 => _14.voices]), () => ( 4));
|
|
956
|
+
_chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, voices, "voices", 128);
|
|
880
957
|
if (voices === 4) {
|
|
881
958
|
return SATB_RANGES.map((range) => ({ ...range }));
|
|
882
959
|
}
|
|
@@ -893,21 +970,26 @@ function resolveRanges(opts) {
|
|
|
893
970
|
return ranges;
|
|
894
971
|
}
|
|
895
972
|
function resolveMaxSpacing(opts) {
|
|
896
|
-
const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
897
|
-
|
|
973
|
+
const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _15 => _15.maxSpacing]), () => ( DEFAULT_MAX_SPACING));
|
|
974
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, maxSpacing, "maxSpacing");
|
|
898
975
|
if (maxSpacing < 0) {
|
|
899
|
-
throw new (0,
|
|
976
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("maxSpacing must be non-negative");
|
|
900
977
|
}
|
|
901
978
|
return maxSpacing;
|
|
902
979
|
}
|
|
980
|
+
function resolveMaxCandidates(opts) {
|
|
981
|
+
if (_optionalChain([opts, 'optionalAccess', _16 => _16.maxCandidates]) === void 0) return void 0;
|
|
982
|
+
_chunkPI3UXRWKcjs.assertPositiveInt.call(void 0, opts.maxCandidates, "maxCandidates", 1e6);
|
|
983
|
+
return opts.maxCandidates;
|
|
984
|
+
}
|
|
903
985
|
function voiceChord(chord, opts) {
|
|
904
986
|
const ranges = resolveRanges(opts);
|
|
905
987
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
906
|
-
const candidates = enumerateVoicings(chord, ranges, maxSpacing);
|
|
988
|
+
const candidates = enumerateVoicings(chord, ranges, maxSpacing, resolveMaxCandidates(opts));
|
|
907
989
|
let best;
|
|
908
990
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
909
991
|
for (const candidate of candidates) {
|
|
910
|
-
let score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess',
|
|
992
|
+
let score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _17 => _17.key]));
|
|
911
993
|
for (let i = 0; i < candidate.length; i += 1) {
|
|
912
994
|
const pitch = candidate[i];
|
|
913
995
|
const range = ranges[i];
|
|
@@ -922,14 +1004,14 @@ function voiceChord(chord, opts) {
|
|
|
922
1004
|
}
|
|
923
1005
|
}
|
|
924
1006
|
if (best === void 0) {
|
|
925
|
-
throw new (0,
|
|
1007
|
+
throw new (0, _chunkPI3UXRWKcjs.NoSolutionError)(
|
|
926
1008
|
`no voicing satisfies the given ranges for ${formatChordSymbol(chord)}`
|
|
927
1009
|
);
|
|
928
1010
|
}
|
|
929
1011
|
return best;
|
|
930
1012
|
}
|
|
931
1013
|
function noSolutionAt(index, chord) {
|
|
932
|
-
return new (0,
|
|
1014
|
+
return new (0, _chunkPI3UXRWKcjs.NoSolutionError)(
|
|
933
1015
|
`no voicing satisfies the given ranges for ${formatChordSymbol(chord)} at index ${index}`,
|
|
934
1016
|
{ at: index }
|
|
935
1017
|
);
|
|
@@ -938,16 +1020,17 @@ function locate(index, chord, work) {
|
|
|
938
1020
|
try {
|
|
939
1021
|
return work();
|
|
940
1022
|
} catch (error) {
|
|
941
|
-
if (error instanceof
|
|
1023
|
+
if (error instanceof _chunkPI3UXRWKcjs.NoSolutionError) {
|
|
942
1024
|
throw noSolutionAt(index, chord);
|
|
943
1025
|
}
|
|
944
1026
|
throw error;
|
|
945
1027
|
}
|
|
946
1028
|
}
|
|
947
1029
|
function voiceProgression(chords, opts) {
|
|
948
|
-
|
|
1030
|
+
_chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, chords.length, "voiced progression chords", _optionalChain([opts, 'optionalAccess', _18 => _18.budget]));
|
|
949
1031
|
const ranges = resolveRanges(opts);
|
|
950
1032
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
1033
|
+
const maxCandidates = resolveMaxCandidates(opts);
|
|
951
1034
|
const result = [];
|
|
952
1035
|
let prev;
|
|
953
1036
|
let prevChord;
|
|
@@ -962,12 +1045,16 @@ function voiceProgression(chords, opts) {
|
|
|
962
1045
|
result.push(prev);
|
|
963
1046
|
continue;
|
|
964
1047
|
}
|
|
965
|
-
const candidates = locate(
|
|
1048
|
+
const candidates = locate(
|
|
1049
|
+
index,
|
|
1050
|
+
chord,
|
|
1051
|
+
() => enumerateVoicings(chord, ranges, maxSpacing, maxCandidates)
|
|
1052
|
+
);
|
|
966
1053
|
let best;
|
|
967
1054
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
968
1055
|
for (const candidate of candidates) {
|
|
969
|
-
const unresolved = prevChord === void 0 ? 0 : resolutionViolations(prev, candidate, prevChord, chord, _optionalChain([opts, 'optionalAccess',
|
|
970
|
-
const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess',
|
|
1056
|
+
const unresolved = prevChord === void 0 ? 0 : resolutionViolations(prev, candidate, prevChord, chord, _optionalChain([opts, 'optionalAccess', _19 => _19.key]));
|
|
1057
|
+
const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _20 => _20.key])) + voiceLeadingCost(prev, candidate) + VIOLATION_PENALTY * violationCount(prev, candidate) + RESOLUTION_PENALTY * unresolved;
|
|
971
1058
|
if (score < bestScore) {
|
|
972
1059
|
bestScore = score;
|
|
973
1060
|
best = candidate;
|
|
@@ -997,7 +1084,7 @@ function dedupePcs(pcs) {
|
|
|
997
1084
|
return result;
|
|
998
1085
|
}
|
|
999
1086
|
function lowestPitchAtOrAbove(pc, floor) {
|
|
1000
|
-
return floor + ((pc -
|
|
1087
|
+
return floor + ((pc - _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, floor)) % 12 + 12) % 12;
|
|
1001
1088
|
}
|
|
1002
1089
|
function nearestPc(target, pcs) {
|
|
1003
1090
|
let best = _nullishCoalesce(pcs[0], () => ( target));
|
|
@@ -1012,17 +1099,17 @@ function nearestPc(target, pcs) {
|
|
|
1012
1099
|
return best;
|
|
1013
1100
|
}
|
|
1014
1101
|
function voiceChordStyled(chord, opts) {
|
|
1015
|
-
const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
1016
|
-
const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
1017
|
-
|
|
1018
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1019
|
-
|
|
1102
|
+
const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _21 => _21.style]), () => ( "close"));
|
|
1103
|
+
const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _22 => _22.octave]), () => ( DEFAULT_STYLE_OCTAVE));
|
|
1104
|
+
_chunkPI3UXRWKcjs.assertInteger.call(void 0, octave, "styled voicing octave", -10, 10);
|
|
1105
|
+
if (_optionalChain([opts, 'optionalAccess', _23 => _23.topNote]) !== void 0) {
|
|
1106
|
+
_chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, opts.topNote, "styled voicing topNote");
|
|
1020
1107
|
}
|
|
1021
1108
|
const base = 12 * (octave + 1);
|
|
1022
|
-
const rootPc =
|
|
1023
|
-
const bassPc =
|
|
1024
|
-
const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess',
|
|
1025
|
-
let order = dedupePcs(chord.intervals.map((interval) =>
|
|
1109
|
+
const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
1110
|
+
const bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
|
|
1111
|
+
const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess', _24 => _24.rootless]) === true;
|
|
1112
|
+
let order = dedupePcs(chord.intervals.map((interval) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + interval)));
|
|
1026
1113
|
const bassIndex = order.indexOf(bassPc);
|
|
1027
1114
|
if (bassIndex > 0) {
|
|
1028
1115
|
order = [...order.slice(bassIndex), ...order.slice(0, bassIndex)];
|
|
@@ -1030,7 +1117,7 @@ function voiceChordStyled(chord, opts) {
|
|
|
1030
1117
|
order = [bassPc, ...order];
|
|
1031
1118
|
}
|
|
1032
1119
|
if (style === "shell") {
|
|
1033
|
-
const roles = order.map((pc) =>
|
|
1120
|
+
const roles = order.map((pc) => _chunkBSLTLGM3cjs.chordToneRole.call(void 0, pc, chord));
|
|
1034
1121
|
const guide = roles.includes("seventh") ? "seventh" : roles.includes("sixth") ? "sixth" : "fifth";
|
|
1035
1122
|
order = order.filter((_pc, i) => {
|
|
1036
1123
|
const role = roles[i];
|
|
@@ -1043,8 +1130,8 @@ function voiceChordStyled(chord, opts) {
|
|
|
1043
1130
|
if (order.length === 0) {
|
|
1044
1131
|
return [];
|
|
1045
1132
|
}
|
|
1046
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1047
|
-
const target = order.includes(
|
|
1133
|
+
if (_optionalChain([opts, 'optionalAccess', _25 => _25.topNote]) !== void 0) {
|
|
1134
|
+
const target = order.includes(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, opts.topNote)) ? _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, opts.topNote) : nearestPc(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, opts.topNote), order);
|
|
1048
1135
|
const targetIndex = order.indexOf(target);
|
|
1049
1136
|
order = [...order.slice(targetIndex + 1), ...order.slice(0, targetIndex + 1)];
|
|
1050
1137
|
}
|
|
@@ -1054,7 +1141,7 @@ function voiceChordStyled(chord, opts) {
|
|
|
1054
1141
|
if (prev === void 0) {
|
|
1055
1142
|
prev = lowestPitchAtOrAbove(pc, base);
|
|
1056
1143
|
} else {
|
|
1057
|
-
let delta = ((pc -
|
|
1144
|
+
let delta = ((pc - _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, prev)) % 12 + 12) % 12;
|
|
1058
1145
|
if (delta === 0) {
|
|
1059
1146
|
delta = 12;
|
|
1060
1147
|
}
|
|
@@ -1068,7 +1155,7 @@ function voiceChordStyled(chord, opts) {
|
|
|
1068
1155
|
stack[stack.length - 3] = (_nullishCoalesce(stack[stack.length - 3], () => ( 0))) - 12;
|
|
1069
1156
|
}
|
|
1070
1157
|
if (chord.bassPc !== void 0) {
|
|
1071
|
-
const bassIndexInStack = stack.findIndex((pitch) =>
|
|
1158
|
+
const bassIndexInStack = stack.findIndex((pitch) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitch) === bassPc);
|
|
1072
1159
|
if (bassIndexInStack >= 0) {
|
|
1073
1160
|
const otherLowest = Math.min(
|
|
1074
1161
|
...stack.filter((_pitch, index) => index !== bassIndexInStack),
|
|
@@ -1083,7 +1170,7 @@ function voiceChordStyled(chord, opts) {
|
|
|
1083
1170
|
const lowest = _nullishCoalesce(voicing[0], () => ( 0));
|
|
1084
1171
|
const highest = _nullishCoalesce(voicing[voicing.length - 1], () => ( 0));
|
|
1085
1172
|
if (lowest < 0 || highest > 127) {
|
|
1086
|
-
throw new (0,
|
|
1173
|
+
throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
|
|
1087
1174
|
`styled voicing at octave ${octave} spans MIDI ${lowest}..${highest}, outside 0..127`
|
|
1088
1175
|
);
|
|
1089
1176
|
}
|
|
@@ -1114,4 +1201,4 @@ function voiceChordStyled(chord, opts) {
|
|
|
1114
1201
|
|
|
1115
1202
|
|
|
1116
1203
|
exports.scaleMatchesChord = scaleMatchesChord; exports.chordScales = chordScales; exports.avoidNotes = avoidNotes; exports.availableTensions = availableTensions; exports.chordScaleReport = chordScaleReport; exports.scalesForChanges = scalesForChanges; exports.spellPitchClass = spellPitchClass; exports.spellScale = spellScale; exports.spellPitchClasses = spellPitchClasses; exports.spellChordFromRoot = spellChordFromRoot; exports.spellChord = spellChord; exports.spellPitch = spellPitch; exports.noteNames = noteNames; exports.parseChordSymbol = parseChordSymbol; exports.formatChordSymbol = formatChordSymbol; exports.transposeChordSymbol = transposeChordSymbol; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled;
|
|
1117
|
-
//# sourceMappingURL=chunk-
|
|
1204
|
+
//# sourceMappingURL=chunk-OL3IHHSJ.cjs.map
|