@libraz/libcantus 1.0.1 → 1.1.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 +27 -65
- package/README_ja.md +28 -66
- package/dist/analyze/index.cjs +11 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +79 -144
- package/dist/analyze/index.d.ts +79 -144
- package/dist/analyze/index.js +20 -16
- package/dist/{chunk-TTCLJX6D.js → chunk-57WB2EDF.js} +10 -8
- package/dist/chunk-57WB2EDF.js.map +1 -0
- package/dist/chunk-5JJ6EHEZ.cjs +457 -0
- package/dist/chunk-5JJ6EHEZ.cjs.map +1 -0
- package/dist/{chunk-AFM2ULJH.cjs → chunk-5KW7DNCJ.cjs} +1567 -1067
- package/dist/chunk-5KW7DNCJ.cjs.map +1 -0
- package/dist/chunk-7GOKQJTQ.js +145 -0
- package/dist/chunk-7GOKQJTQ.js.map +1 -0
- package/dist/{chunk-H3VORETI.js → chunk-AWEMD747.js} +487 -592
- package/dist/chunk-AWEMD747.js.map +1 -0
- package/dist/{chunk-PKFNUHDY.js → chunk-DKICR7AN.js} +1216 -611
- package/dist/chunk-DKICR7AN.js.map +1 -0
- package/dist/{chunk-INHVXP6Z.js → chunk-FPBZFNCD.js} +1159 -415
- package/dist/chunk-FPBZFNCD.js.map +1 -0
- package/dist/{chunk-6XH6QZYQ.cjs → chunk-GDKE6RQW.cjs} +1453 -848
- package/dist/chunk-GDKE6RQW.cjs.map +1 -0
- package/dist/chunk-H66ARFU5.js +363 -0
- package/dist/chunk-H66ARFU5.js.map +1 -0
- package/dist/{chunk-YH3BIKHH.js → chunk-HOHFYGKW.js} +146 -49
- package/dist/chunk-HOHFYGKW.js.map +1 -0
- package/dist/chunk-HSY2VQXZ.js +2272 -0
- package/dist/chunk-HSY2VQXZ.js.map +1 -0
- package/dist/{chunk-E3F4AHAH.cjs → chunk-J772DL3B.cjs} +221 -134
- package/dist/chunk-J772DL3B.cjs.map +1 -0
- package/dist/chunk-K6OZXIR6.js +1 -0
- package/dist/{chunk-VTI6FOLB.js → chunk-PQL7E5F6.js} +709 -133
- package/dist/chunk-PQL7E5F6.js.map +1 -0
- package/dist/chunk-Q6R3QAZD.cjs +3329 -0
- package/dist/chunk-Q6R3QAZD.cjs.map +1 -0
- package/dist/{chunk-MXO72PLY.cjs → chunk-QCXYKSZS.cjs} +21 -19
- package/dist/chunk-QCXYKSZS.cjs.map +1 -0
- package/dist/{chunk-VOHZB45S.js → chunk-R5VUXQMP.js} +1321 -821
- package/dist/chunk-R5VUXQMP.js.map +1 -0
- package/dist/chunk-R7B5N2JK.cjs +363 -0
- package/dist/chunk-R7B5N2JK.cjs.map +1 -0
- package/dist/{chunk-WWURE4XT.cjs → chunk-RVS4G4MR.cjs} +624 -729
- package/dist/chunk-RVS4G4MR.cjs.map +1 -0
- package/dist/chunk-TT6PKMPR.cjs +145 -0
- package/dist/chunk-TT6PKMPR.cjs.map +1 -0
- package/dist/chunk-UGLFDG7L.cjs +1 -0
- package/dist/chunk-UGLFDG7L.cjs.map +1 -0
- package/dist/chunk-VLE5R7PF.js +3329 -0
- package/dist/chunk-VLE5R7PF.js.map +1 -0
- package/dist/{chunk-FVSI36UV.js → chunk-X6RJAHII.js} +197 -110
- package/dist/chunk-X6RJAHII.js.map +1 -0
- package/dist/{chunk-Q2WGKJNN.cjs → chunk-XKSO2V5I.cjs} +699 -123
- package/dist/chunk-XKSO2V5I.cjs.map +1 -0
- package/dist/chunk-XVMJO3QF.cjs +2272 -0
- package/dist/chunk-XVMJO3QF.cjs.map +1 -0
- package/dist/{chunk-UADYAYAT.cjs → chunk-YPB3A3EY.cjs} +1309 -565
- package/dist/chunk-YPB3A3EY.cjs.map +1 -0
- package/dist/{index-C_8o--VE.d.cts → coerce-CJlgULrQ.d.cts} +210 -199
- package/dist/{index-szBv3496.d.ts → coerce-wwdELNYS.d.ts} +210 -199
- package/dist/core/index.cjs +9 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +122 -108
- package/dist/core/index.d.ts +122 -108
- package/dist/core/index.js +32 -28
- package/dist/generate/index.cjs +8 -8
- package/dist/generate/index.d.cts +168 -84
- package/dist/generate/index.d.ts +168 -84
- package/dist/generate/index.js +7 -7
- package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
- package/dist/{index-Dzr13q3d.d.cts → index-B3WCj_Hm.d.cts} +135 -58
- package/dist/{index-BO6xE9YU.d.cts → index-BUtIf2go.d.ts} +460 -254
- package/dist/{index-9wcMhG73.d.ts → index-BaoKVVhE.d.cts} +293 -56
- package/dist/{index-Chbst3Jx.d.ts → index-BpWOhkkE.d.ts} +135 -58
- package/dist/{index-CgHqxdps.d.ts → index-BzLNkmA7.d.cts} +196 -114
- package/dist/{index-CunYKLiu.d.ts → index-CBCP84rv.d.cts} +1 -1
- package/dist/{index-BkGmlyI3.d.cts → index-Ci3xyows.d.ts} +60 -23
- package/dist/{index-CCNJRG-P.d.cts → index-CnHQgqNh.d.ts} +293 -56
- package/dist/{index-B4o55NDr.d.cts → index-CnIdP4EZ.d.ts} +1 -1
- package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
- package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
- package/dist/{index-D57QRbVn.d.ts → index-DTsKaPaC.d.cts} +460 -254
- package/dist/{index-Cj49F892.d.ts → index-D_hzxD8w.d.cts} +60 -23
- package/dist/{index-BGFW-8P2.d.cts → index-DpC-mDlV.d.ts} +196 -114
- package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
- package/dist/index.cjs +42 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.js +82 -54
- package/dist/{masks-DYbE485J.d.ts → masks-C8TWLFIi.d.cts} +49 -2
- package/dist/{masks-DYbE485J.d.cts → masks-O73Rhj5d.d.ts} +49 -2
- package/dist/model/index.cjs +19 -12
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +502 -181
- package/dist/model/index.d.ts +502 -181
- package/dist/model/index.js +18 -11
- package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
- package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
- package/dist/theory/index.cjs +35 -6
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +257 -62
- package/dist/theory/index.d.ts +257 -62
- package/dist/theory/index.js +41 -12
- package/package.json +6 -5
- package/dist/chunk-2LCPM3EG.js +0 -3224
- package/dist/chunk-2LCPM3EG.js.map +0 -1
- package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
- package/dist/chunk-6XPTBE7Y.cjs +0 -49
- package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
- package/dist/chunk-AFM2ULJH.cjs.map +0 -1
- package/dist/chunk-B6XNKWZV.cjs +0 -3224
- package/dist/chunk-B6XNKWZV.cjs.map +0 -1
- package/dist/chunk-E3F4AHAH.cjs.map +0 -1
- package/dist/chunk-ELCTZUJJ.js +0 -379
- package/dist/chunk-ELCTZUJJ.js.map +0 -1
- package/dist/chunk-FVSI36UV.js.map +0 -1
- package/dist/chunk-H3VORETI.js.map +0 -1
- package/dist/chunk-H42UHPYW.js +0 -1577
- package/dist/chunk-H42UHPYW.js.map +0 -1
- package/dist/chunk-HFPS7JFD.js +0 -1
- package/dist/chunk-INHVXP6Z.js.map +0 -1
- package/dist/chunk-K4ZJUGF4.cjs +0 -379
- package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
- package/dist/chunk-KNJEKLKV.js +0 -49
- package/dist/chunk-KNJEKLKV.js.map +0 -1
- package/dist/chunk-MXO72PLY.cjs.map +0 -1
- package/dist/chunk-NLEU4QSE.cjs +0 -360
- package/dist/chunk-NLEU4QSE.cjs.map +0 -1
- package/dist/chunk-OE6WK2Y2.cjs +0 -1577
- package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
- package/dist/chunk-PKFNUHDY.js.map +0 -1
- package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
- package/dist/chunk-RHIJ3S4I.cjs +0 -1
- package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
- package/dist/chunk-TTCLJX6D.js.map +0 -1
- package/dist/chunk-UADYAYAT.cjs.map +0 -1
- package/dist/chunk-VOHZB45S.js.map +0 -1
- package/dist/chunk-VTI6FOLB.js.map +0 -1
- package/dist/chunk-WWURE4XT.cjs.map +0 -1
- package/dist/chunk-YH3BIKHH.js.map +0 -1
- /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
|
|
@@ -34,6 +33,7 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
|
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
|
|
36
|
+
var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
|
|
@@ -44,7 +44,18 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
var _chunkXKSO2V5Icjs = require('./chunk-XKSO2V5I.cjs');
|
|
48
59
|
|
|
49
60
|
// src/theory/transposition/index.ts
|
|
50
61
|
var TRANSPOSING_INSTRUMENTS = Object.freeze({
|
|
@@ -74,18 +85,22 @@ var TRANSPOSING_INSTRUMENTS = Object.freeze({
|
|
|
74
85
|
contrabassoon: "-P8",
|
|
75
86
|
guitar: "-P8"
|
|
76
87
|
});
|
|
88
|
+
var PROFILE_NAME_ALIASES = Object.freeze({
|
|
89
|
+
"guitar (drop D)": "guitar"
|
|
90
|
+
});
|
|
77
91
|
function instrumentTransposition(instrument) {
|
|
78
92
|
if (typeof instrument !== "string") {
|
|
79
|
-
return
|
|
93
|
+
return _chunkXKSO2V5Icjs.toSpelledInterval.call(void 0, instrument);
|
|
80
94
|
}
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
const name = Object.hasOwn(PROFILE_NAME_ALIASES, instrument) ? PROFILE_NAME_ALIASES[instrument] : instrument;
|
|
96
|
+
if (Object.hasOwn(TRANSPOSING_INSTRUMENTS, name)) {
|
|
97
|
+
return _chunkXKSO2V5Icjs.parseInterval.call(void 0, TRANSPOSING_INSTRUMENTS[name]);
|
|
83
98
|
}
|
|
84
99
|
try {
|
|
85
|
-
return
|
|
100
|
+
return _chunkXKSO2V5Icjs.parseInterval.call(void 0, instrument);
|
|
86
101
|
} catch (error) {
|
|
87
|
-
if (error instanceof
|
|
88
|
-
throw new (0,
|
|
102
|
+
if (error instanceof _chunkXKSO2V5Icjs.InvalidInputError) {
|
|
103
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
89
104
|
`unknown transposing instrument ${JSON.stringify(instrument)}; pass a name from TRANSPOSING_INSTRUMENTS or an interval such as '-M2'`
|
|
90
105
|
);
|
|
91
106
|
}
|
|
@@ -93,332 +108,24 @@ function instrumentTransposition(instrument) {
|
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
function reversedInterval(step) {
|
|
96
|
-
|
|
111
|
+
return {
|
|
97
112
|
number: step.number,
|
|
98
113
|
quality: step.quality,
|
|
99
114
|
// Negating a zero span yields -0, which compares unequal to 0 under Object.is.
|
|
100
|
-
semitones: step.semitones === 0 ? 0 : -step.semitones
|
|
115
|
+
semitones: step.semitones === 0 ? 0 : -step.semitones,
|
|
116
|
+
descending: !step.descending
|
|
101
117
|
};
|
|
102
|
-
if (!(_nullishCoalesce(step.descending, () => ( step.semitones < 0)))) {
|
|
103
|
-
reversed.descending = true;
|
|
104
|
-
}
|
|
105
|
-
return reversed;
|
|
106
118
|
}
|
|
107
119
|
function toSoundingPitch(note, instrument) {
|
|
108
|
-
return
|
|
120
|
+
return _chunkXKSO2V5Icjs.transposeByInterval.call(void 0, _chunkXKSO2V5Icjs.toNoteData.call(void 0, note), instrumentTransposition(instrument));
|
|
109
121
|
}
|
|
110
122
|
function toWrittenPitch(note, instrument) {
|
|
111
|
-
return
|
|
112
|
-
|
|
123
|
+
return _chunkXKSO2V5Icjs.transposeByInterval.call(void 0,
|
|
124
|
+
_chunkXKSO2V5Icjs.toNoteData.call(void 0, note),
|
|
113
125
|
reversedInterval(instrumentTransposition(instrument))
|
|
114
126
|
);
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
// src/theory/chordscale/index.ts
|
|
118
|
-
function popcount12(mask) {
|
|
119
|
-
let count = 0;
|
|
120
|
-
for (let n = 0; n < 12; n += 1) {
|
|
121
|
-
count += mask >> n & 1;
|
|
122
|
-
}
|
|
123
|
-
return count;
|
|
124
|
-
}
|
|
125
|
-
function maskHasPitchClass(mask, scaleRootPc, pc) {
|
|
126
|
-
const offset = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - scaleRootPc);
|
|
127
|
-
return (mask >> offset & 1) === 1;
|
|
128
|
-
}
|
|
129
|
-
function scaleMatchesChord(chordPcs, scaleMask, scaleRootPc) {
|
|
130
|
-
for (const pc of chordPcs) {
|
|
131
|
-
if (!maskHasPitchClass(scaleMask, scaleRootPc, pc)) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
var ALIASED_SCALE_NAMES = /* @__PURE__ */ new Set(["major", "naturalMinor"]);
|
|
138
|
-
var HEPTATONIC_SIZE = 7;
|
|
139
|
-
var SCALE_PREFERENCE = [
|
|
140
|
-
"ionian",
|
|
141
|
-
"major",
|
|
142
|
-
"mixolydian",
|
|
143
|
-
"dorian",
|
|
144
|
-
"lydian",
|
|
145
|
-
"aeolian",
|
|
146
|
-
"naturalMinor",
|
|
147
|
-
"phrygian",
|
|
148
|
-
"locrian"
|
|
149
|
-
];
|
|
150
|
-
var IDIOMATIC_SCALES = {
|
|
151
|
-
aug: ["wholeTone"],
|
|
152
|
-
m7b5: ["locrian"],
|
|
153
|
-
// The diminished scale played over a diminished seventh is the whole-half
|
|
154
|
-
// octatonic. Its half-whole rotation belongs to the dominant it colours: over
|
|
155
|
-
// a diminished seventh every one of its non-chord tones lands a semitone
|
|
156
|
-
// above a chord tone, so it offers no tension at all.
|
|
157
|
-
dim7: ["octatonicWholeHalf"],
|
|
158
|
-
minMaj7: ["melodicMinor"]
|
|
159
|
-
};
|
|
160
|
-
function conventionalRank(name) {
|
|
161
|
-
const index = SCALE_PREFERENCE.indexOf(name);
|
|
162
|
-
return index < 0 ? SCALE_PREFERENCE.length : index;
|
|
163
|
-
}
|
|
164
|
-
function idiomaticRank(chord, name) {
|
|
165
|
-
const choices = IDIOMATIC_SCALES[chord.quality];
|
|
166
|
-
if (choices === void 0) {
|
|
167
|
-
return 0;
|
|
168
|
-
}
|
|
169
|
-
const index = choices.indexOf(name);
|
|
170
|
-
return index < 0 ? choices.length + 1 : index;
|
|
171
|
-
}
|
|
172
|
-
function isAlteredDominant(chord, scaleName) {
|
|
173
|
-
return chord.quality === "dom7" && scaleName === "altered";
|
|
174
|
-
}
|
|
175
|
-
function chordScales(chord) {
|
|
176
|
-
const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
|
|
177
|
-
const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
|
|
178
|
-
const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
|
|
179
|
-
const ranked = [];
|
|
180
|
-
const seenMasks = /* @__PURE__ */ new Set();
|
|
181
|
-
for (const name of Object.keys(_chunkUADYAYATcjs.NAMED_SCALES)) {
|
|
182
|
-
if (name === "chromatic" || ALIASED_SCALE_NAMES.has(name)) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
const mask = _chunkUADYAYATcjs.namedScaleMask.call(void 0, name);
|
|
186
|
-
if (mask === void 0 || seenMasks.has(mask)) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
seenMasks.add(mask);
|
|
190
|
-
if (scaleMatchesChord(chordPcs, mask, rootPc) || isAlteredDominant(data, name)) {
|
|
191
|
-
const size = popcount12(mask);
|
|
192
|
-
ranked.push({
|
|
193
|
-
name,
|
|
194
|
-
extra: size - chordPcs.length,
|
|
195
|
-
size,
|
|
196
|
-
avoid: avoidNotes(data, name).length
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (ranked.length === 0) {
|
|
201
|
-
if (_chunkUADYAYATcjs.NAMED_SCALES.chromatic !== void 0) {
|
|
202
|
-
return [{ name: "chromatic", rootPc }];
|
|
203
|
-
}
|
|
204
|
-
return [];
|
|
205
|
-
}
|
|
206
|
-
const preferHeptatonic = chordPcs.length <= 3;
|
|
207
|
-
ranked.sort((a, b) => {
|
|
208
|
-
const idiomaticA = idiomaticRank(data, a.name);
|
|
209
|
-
const idiomaticB = idiomaticRank(data, b.name);
|
|
210
|
-
if (idiomaticA !== idiomaticB) {
|
|
211
|
-
return idiomaticA - idiomaticB;
|
|
212
|
-
}
|
|
213
|
-
if (preferHeptatonic && a.size === HEPTATONIC_SIZE !== (b.size === HEPTATONIC_SIZE)) {
|
|
214
|
-
return a.size === HEPTATONIC_SIZE ? -1 : 1;
|
|
215
|
-
}
|
|
216
|
-
if (a.extra !== b.extra) {
|
|
217
|
-
return a.extra - b.extra;
|
|
218
|
-
}
|
|
219
|
-
const rankA = conventionalRank(a.name);
|
|
220
|
-
const rankB = conventionalRank(b.name);
|
|
221
|
-
if (rankA !== rankB) {
|
|
222
|
-
return rankA - rankB;
|
|
223
|
-
}
|
|
224
|
-
if (a.avoid !== b.avoid) {
|
|
225
|
-
return a.avoid - b.avoid;
|
|
226
|
-
}
|
|
227
|
-
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
228
|
-
});
|
|
229
|
-
return ranked.map((entry) => ({ name: entry.name, rootPc }));
|
|
230
|
-
}
|
|
231
|
-
function suspendedTone(chordPcs, rootPc) {
|
|
232
|
-
const sounds = (semitones) => chordPcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones));
|
|
233
|
-
if (sounds(3) || sounds(4)) {
|
|
234
|
-
return void 0;
|
|
235
|
-
}
|
|
236
|
-
if (sounds(5)) {
|
|
237
|
-
return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 5);
|
|
238
|
-
}
|
|
239
|
-
if (sounds(2)) {
|
|
240
|
-
return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 2);
|
|
241
|
-
}
|
|
242
|
-
return void 0;
|
|
243
|
-
}
|
|
244
|
-
function avoidNotes(chord, scaleName, opts = {}) {
|
|
245
|
-
const mask = _chunkUADYAYATcjs.requireScaleMask.call(void 0, scaleName);
|
|
246
|
-
const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
|
|
247
|
-
const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
|
|
248
|
-
const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
|
|
249
|
-
const alteredDominant = isAlteredDominant(data, scaleName);
|
|
250
|
-
if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
const chordSet = new Set(chordPcs);
|
|
254
|
-
const suspended = suspendedTone(chordPcs, rootPc);
|
|
255
|
-
const displacedThird = suspended === void 0 ? void 0 : _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, suspended - 1);
|
|
256
|
-
const melodic = opts.use === "melodic";
|
|
257
|
-
const semitoneAboveRoot = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 1);
|
|
258
|
-
const avoid = [];
|
|
259
|
-
for (let pc = 0; pc < 12; pc += 1) {
|
|
260
|
-
if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
if (melodic && pc !== semitoneAboveRoot) {
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
if (!alteredDominant && (chordSet.has(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - 1)) || pc === displacedThird)) {
|
|
267
|
-
avoid.push(pc);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return avoid.sort((a, b) => a - b);
|
|
271
|
-
}
|
|
272
|
-
var MINOR_RESOLUTION_TENSIONS = [1, 3, 8];
|
|
273
|
-
function resolvesToMinorTonic(chord, chordPcs, target) {
|
|
274
|
-
if (target === void 0) {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
278
|
-
const sounds = (semitones) => chordPcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones));
|
|
279
|
-
if (!sounds(4) || !sounds(10)) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
const targetRootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, target.rootPc);
|
|
283
|
-
if (targetRootPc !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 5)) {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
return _chunkUADYAYATcjs.chordPitchClasses.call(void 0, target).includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, targetRootPc + 3));
|
|
287
|
-
}
|
|
288
|
-
function availableTensions(chord, scaleName, opts = {}) {
|
|
289
|
-
const mask = _chunkUADYAYATcjs.requireScaleMask.call(void 0, scaleName);
|
|
290
|
-
const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
|
|
291
|
-
const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
|
|
292
|
-
const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
|
|
293
|
-
const alteredDominant = isAlteredDominant(data, scaleName);
|
|
294
|
-
if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
|
|
295
|
-
return [];
|
|
296
|
-
}
|
|
297
|
-
const chordSet = new Set(chordPcs);
|
|
298
|
-
const avoidSet = alteredDominant ? /* @__PURE__ */ new Set() : new Set(avoidNotes(data, scaleName));
|
|
299
|
-
const functional = resolvesToMinorTonic(
|
|
300
|
-
data,
|
|
301
|
-
chordPcs,
|
|
302
|
-
opts.resolvesTo === void 0 ? void 0 : _chunkUADYAYATcjs.toChordData.call(void 0, opts.resolvesTo)
|
|
303
|
-
) ? new Set(MINOR_RESOLUTION_TENSIONS.map((semitones) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones))) : void 0;
|
|
304
|
-
const tensions = [];
|
|
305
|
-
for (let pc = 0; pc < 12; pc += 1) {
|
|
306
|
-
if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (avoidSet.has(pc) && _optionalChain([functional, 'optionalAccess', _2 => _2.has, 'call', _3 => _3(pc)]) !== true) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
tensions.push(pc);
|
|
313
|
-
}
|
|
314
|
-
return tensions.sort((a, b) => a - b);
|
|
315
|
-
}
|
|
316
|
-
function chordScaleReport(chord, limit) {
|
|
317
|
-
if (limit !== void 0) {
|
|
318
|
-
_chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, limit, "chord-scale report limit");
|
|
319
|
-
}
|
|
320
|
-
const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
|
|
321
|
-
const matches = chordScales(data);
|
|
322
|
-
const chosen = limit === void 0 ? matches : matches.slice(0, limit);
|
|
323
|
-
return chosen.map((match) => {
|
|
324
|
-
const harmonic = avoidNotes(data, match.name);
|
|
325
|
-
const melodic = new Set(avoidNotes(data, match.name, { use: "melodic" }));
|
|
326
|
-
return {
|
|
327
|
-
name: match.name,
|
|
328
|
-
rootPc: match.rootPc,
|
|
329
|
-
avoid: harmonic.filter((pc) => melodic.has(pc)),
|
|
330
|
-
passing: harmonic.filter((pc) => !melodic.has(pc)),
|
|
331
|
-
tensions: availableTensions(data, match.name)
|
|
332
|
-
};
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
var RANK_PENALTY = 0.01;
|
|
336
|
-
function scalePitchClassSet(name, rootPc) {
|
|
337
|
-
const mask = _chunkUADYAYATcjs.namedScaleMask.call(void 0, name);
|
|
338
|
-
const pcs = /* @__PURE__ */ new Set();
|
|
339
|
-
if (mask === void 0) {
|
|
340
|
-
return pcs;
|
|
341
|
-
}
|
|
342
|
-
for (let n = 0; n < 12; n += 1) {
|
|
343
|
-
if ((mask >> n & 1) === 1) {
|
|
344
|
-
pcs.add(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + n));
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return pcs;
|
|
348
|
-
}
|
|
349
|
-
function symmetricDifferenceSize(a, b) {
|
|
350
|
-
let count = 0;
|
|
351
|
-
for (const pc of a) {
|
|
352
|
-
if (!b.has(pc)) {
|
|
353
|
-
count += 1;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
for (const pc of b) {
|
|
357
|
-
if (!a.has(pc)) {
|
|
358
|
-
count += 1;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return count;
|
|
362
|
-
}
|
|
363
|
-
function scalesForChanges(chords) {
|
|
364
|
-
_chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, chords.length, "chord-scale changes");
|
|
365
|
-
if (chords.length === 0) {
|
|
366
|
-
return [];
|
|
367
|
-
}
|
|
368
|
-
const candidateLists = chords.map((chord) => chordScales(chord));
|
|
369
|
-
const pcSets = candidateLists.map(
|
|
370
|
-
(candidates) => candidates.map((match) => scalePitchClassSet(match.name, match.rootPc))
|
|
371
|
-
);
|
|
372
|
-
const first = _nullishCoalesce(candidateLists[0], () => ( []));
|
|
373
|
-
const dp = [first.map((_, j) => j * RANK_PENALTY)];
|
|
374
|
-
const back = [first.map(() => -1)];
|
|
375
|
-
for (let i = 1; i < candidateLists.length; i += 1) {
|
|
376
|
-
const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
|
|
377
|
-
const prevCosts = _nullishCoalesce(dp[i - 1], () => ( []));
|
|
378
|
-
const prevSets = _nullishCoalesce(pcSets[i - 1], () => ( []));
|
|
379
|
-
const curSets = _nullishCoalesce(pcSets[i], () => ( []));
|
|
380
|
-
const stageCosts = [];
|
|
381
|
-
const stageBack = [];
|
|
382
|
-
for (let j = 0; j < candidates.length; j += 1) {
|
|
383
|
-
let bestCost = Number.POSITIVE_INFINITY;
|
|
384
|
-
let bestPrev = -1;
|
|
385
|
-
for (let k = 0; k < prevCosts.length; k += 1) {
|
|
386
|
-
const transition = symmetricDifferenceSize(
|
|
387
|
-
_nullishCoalesce(prevSets[k], () => ( /* @__PURE__ */ new Set())),
|
|
388
|
-
_nullishCoalesce(curSets[j], () => ( /* @__PURE__ */ new Set()))
|
|
389
|
-
);
|
|
390
|
-
const cost = (_nullishCoalesce(prevCosts[k], () => ( 0))) + transition;
|
|
391
|
-
if (cost < bestCost) {
|
|
392
|
-
bestCost = cost;
|
|
393
|
-
bestPrev = k;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
stageCosts.push(bestCost + j * RANK_PENALTY);
|
|
397
|
-
stageBack.push(bestPrev);
|
|
398
|
-
}
|
|
399
|
-
dp.push(stageCosts);
|
|
400
|
-
back.push(stageBack);
|
|
401
|
-
}
|
|
402
|
-
const lastCosts = _nullishCoalesce(dp[dp.length - 1], () => ( []));
|
|
403
|
-
let bestFinal = 0;
|
|
404
|
-
for (let j = 1; j < lastCosts.length; j += 1) {
|
|
405
|
-
if ((_nullishCoalesce(lastCosts[j], () => ( Number.POSITIVE_INFINITY))) < (_nullishCoalesce(lastCosts[bestFinal], () => ( 0)))) {
|
|
406
|
-
bestFinal = j;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
const chosenIndices = new Array(candidateLists.length);
|
|
410
|
-
let current = bestFinal;
|
|
411
|
-
for (let i = candidateLists.length - 1; i >= 0; i -= 1) {
|
|
412
|
-
chosenIndices[i] = current;
|
|
413
|
-
current = _nullishCoalesce(_optionalChain([back, 'access', _4 => _4[i], 'optionalAccess', _5 => _5[current]]), () => ( -1));
|
|
414
|
-
}
|
|
415
|
-
return chords.map((chord, i) => {
|
|
416
|
-
const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
|
|
417
|
-
const scale = _nullishCoalesce(candidates[_nullishCoalesce(chosenIndices[i], () => ( 0))], () => ( { name: "chromatic", rootPc: chord.rootPc }));
|
|
418
|
-
return { chord, scale };
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
129
|
// src/theory/figured-bass/index.ts
|
|
423
130
|
var MIN_FIGURE_NUMBER = 2;
|
|
424
131
|
var MAX_FIGURE_NUMBER = 9;
|
|
@@ -433,7 +140,7 @@ var DOUBLE_ACCIDENTALS = {
|
|
|
433
140
|
var SINGLE_ACCIDENTALS = {
|
|
434
141
|
"#": "sharp",
|
|
435
142
|
"\u266F": "sharp",
|
|
436
|
-
"+": "
|
|
143
|
+
"+": "raised",
|
|
437
144
|
b: "flat",
|
|
438
145
|
"\u266D": "flat",
|
|
439
146
|
n: "natural",
|
|
@@ -441,31 +148,28 @@ var SINGLE_ACCIDENTALS = {
|
|
|
441
148
|
};
|
|
442
149
|
var FIGURE_SEPARATORS = /* @__PURE__ */ new Set([" ", " ", ",", "/"]);
|
|
443
150
|
var MOTION_MARKS = /* @__PURE__ */ new Set(["-", "\u2013"]);
|
|
444
|
-
var
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
"
|
|
448
|
-
"
|
|
449
|
-
|
|
151
|
+
var PRINTED_ACCIDENTALS = [
|
|
152
|
+
["natural", "n", 0],
|
|
153
|
+
["sharp", "#", 1],
|
|
154
|
+
["doubleSharp", "##", 2],
|
|
155
|
+
["flat", "b", -1],
|
|
156
|
+
["doubleFlat", "bb", -2]
|
|
157
|
+
];
|
|
158
|
+
var PRINTED_ALTERS = new Map(
|
|
159
|
+
PRINTED_ACCIDENTALS.map(([accidental, , alter]) => [accidental, alter])
|
|
160
|
+
);
|
|
161
|
+
var ACCIDENTAL_GLYPHS = new Map(
|
|
162
|
+
PRINTED_ACCIDENTALS.map(([, glyph, alter]) => [alter, glyph])
|
|
163
|
+
);
|
|
450
164
|
function invalidFigures(figures, detail) {
|
|
451
|
-
return new (0,
|
|
165
|
+
return new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, figures)} ${detail}`);
|
|
452
166
|
}
|
|
453
167
|
function alteredBy(diatonicAlter, accidental) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
case "doubleSharp":
|
|
458
|
-
return diatonicAlter + 2;
|
|
459
|
-
case "flat":
|
|
460
|
-
return diatonicAlter - 1;
|
|
461
|
-
case "doubleFlat":
|
|
462
|
-
return diatonicAlter - 2;
|
|
463
|
-
// A natural sign names the natural note, whatever the key would have given.
|
|
464
|
-
case "natural":
|
|
465
|
-
return 0;
|
|
466
|
-
default:
|
|
467
|
-
return diatonicAlter;
|
|
168
|
+
const printed = PRINTED_ALTERS.get(accidental);
|
|
169
|
+
if (printed !== void 0) {
|
|
170
|
+
return printed;
|
|
468
171
|
}
|
|
172
|
+
return accidental === "raised" ? diatonicAlter + 1 : diatonicAlter;
|
|
469
173
|
}
|
|
470
174
|
function readFigure(text, start) {
|
|
471
175
|
let index = start;
|
|
@@ -564,8 +268,8 @@ var FIGURE_ABBREVIATIONS = /* @__PURE__ */ new Map([
|
|
|
564
268
|
]);
|
|
565
269
|
function tertianQualityIndex() {
|
|
566
270
|
const index = /* @__PURE__ */ new Map();
|
|
567
|
-
for (const quality of
|
|
568
|
-
const { intervals } =
|
|
271
|
+
for (const quality of _chunkYPB3A3EYcjs.chordQualities.call(void 0, )) {
|
|
272
|
+
const { intervals } = _chunkYPB3A3EYcjs.makeChord.call(void 0, 0, quality);
|
|
569
273
|
const template = intervals.join(",");
|
|
570
274
|
if (intervals.length >= 3 && intervals.length <= MAX_STACK_SIZE && !index.has(template)) {
|
|
571
275
|
index.set(template, quality);
|
|
@@ -575,22 +279,22 @@ function tertianQualityIndex() {
|
|
|
575
279
|
}
|
|
576
280
|
var TERTIAN_QUALITIES = tertianQualityIndex();
|
|
577
281
|
function keyLetterAlters(key) {
|
|
578
|
-
const size =
|
|
282
|
+
const size = _chunkYPB3A3EYcjs.scaleTonesInDegreeOrder.call(void 0, key.scale).length;
|
|
579
283
|
if (size !== 7) {
|
|
580
|
-
throw new (0,
|
|
284
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass requires a heptatonic key (received ${size} tones)`);
|
|
581
285
|
}
|
|
582
286
|
const alters = [];
|
|
583
|
-
for (const note of
|
|
584
|
-
alters[
|
|
287
|
+
for (const note of _chunkYPB3A3EYcjs.spellScale.call(void 0, key.tonic, key.scale)) {
|
|
288
|
+
alters[_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, note.letter)] = note.alter;
|
|
585
289
|
}
|
|
586
290
|
return alters;
|
|
587
291
|
}
|
|
588
292
|
function noteAbove(bass, figure, alters) {
|
|
589
|
-
const letter =
|
|
293
|
+
const letter = _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, bass.letter + figure.number - 1);
|
|
590
294
|
return { letter, alter: alteredBy(_nullishCoalesce(alters[letter], () => ( 0)), figure.accidental) };
|
|
591
295
|
}
|
|
592
296
|
function bareNote(note) {
|
|
593
|
-
return { letter:
|
|
297
|
+
return { letter: _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, note.letter), alter: note.alter };
|
|
594
298
|
}
|
|
595
299
|
function realizeSonority(bass, resolved, text, alters) {
|
|
596
300
|
const numbers = [...new Set(resolved.map((figure) => figure.number))].sort((a, b) => a - b);
|
|
@@ -604,36 +308,35 @@ function realizeSonority(bass, resolved, text, alters) {
|
|
|
604
308
|
);
|
|
605
309
|
const tones = [bass, ...notes];
|
|
606
310
|
const root = _nullishCoalesce(tones[[1, ...sonority.intervals].indexOf(sonority.rootInterval)], () => ( bass));
|
|
607
|
-
const rootPc =
|
|
311
|
+
const rootPc = _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, root);
|
|
608
312
|
const stack = [];
|
|
609
313
|
for (let step = 0; step < tones.length; step += 1) {
|
|
610
|
-
const tone = tones.find((candidate) =>
|
|
314
|
+
const tone = tones.find((candidate) => _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, candidate.letter - root.letter) === step * 2);
|
|
611
315
|
if (tone === void 0) {
|
|
612
|
-
throw new (0,
|
|
613
|
-
`figured bass ${
|
|
316
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
317
|
+
`figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, text)} sounds ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")}, which does not stack in thirds`
|
|
614
318
|
);
|
|
615
319
|
}
|
|
616
|
-
stack.push(
|
|
320
|
+
stack.push(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, tone) - rootPc));
|
|
617
321
|
}
|
|
618
322
|
const quality = TERTIAN_QUALITIES.get(stack.join(","));
|
|
619
323
|
if (quality === void 0) {
|
|
620
|
-
throw new (0,
|
|
621
|
-
`figured bass ${
|
|
324
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
325
|
+
`figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, text)} sounds ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")}, which no chord quality names`
|
|
622
326
|
);
|
|
623
327
|
}
|
|
624
|
-
const chord =
|
|
328
|
+
const chord = _chunkYPB3A3EYcjs.makeChord.call(void 0, rootPc, quality, _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bass));
|
|
625
329
|
chord.rootSpelling = { letter: root.letter, alter: root.alter };
|
|
626
330
|
chord.bassSpelling = { letter: bass.letter, alter: bass.alter };
|
|
627
331
|
return { chord, notes };
|
|
628
332
|
}
|
|
629
333
|
function figuredBassRealization(bass, figures, key) {
|
|
630
334
|
if (typeof figures !== "string") {
|
|
631
|
-
throw new (0,
|
|
335
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass must be a string; received ${typeof figures}`);
|
|
632
336
|
}
|
|
633
|
-
const bassData =
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
_chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, bassData);
|
|
337
|
+
const bassData = _chunkXKSO2V5Icjs.toNoteData.call(void 0, bass);
|
|
338
|
+
const alters = keyLetterAlters(_chunkYPB3A3EYcjs.resolveKey.call(void 0, key));
|
|
339
|
+
_chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bassData);
|
|
637
340
|
const bassNote = bareNote(bassData);
|
|
638
341
|
const text = figures.trim();
|
|
639
342
|
const { figures: written, motions } = parseFigures(text);
|
|
@@ -654,90 +357,170 @@ function realizeFiguredBass(bass, figures, key) {
|
|
|
654
357
|
return figuredBassRealization(bass, figures, key).chord;
|
|
655
358
|
}
|
|
656
359
|
function figuredBassOf(chord, key) {
|
|
657
|
-
const data =
|
|
658
|
-
const
|
|
659
|
-
const alters = keyLetterAlters(
|
|
660
|
-
const tones =
|
|
661
|
-
const bassPc =
|
|
662
|
-
const bass = tones.find((tone) =>
|
|
360
|
+
const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
|
|
361
|
+
const resolved = _chunkYPB3A3EYcjs.resolveKey.call(void 0, key);
|
|
362
|
+
const alters = keyLetterAlters(resolved);
|
|
363
|
+
const tones = _chunkYPB3A3EYcjs.spellChord.call(void 0, data, resolved.tonic, resolved.scale);
|
|
364
|
+
const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(data.bassPc, () => ( data.rootPc)));
|
|
365
|
+
const bass = tones.find((tone) => _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, tone) === bassPc);
|
|
663
366
|
if (bass === void 0) {
|
|
664
|
-
throw new (0,
|
|
367
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
665
368
|
"figured bass measures intervals above a chord tone, and this chord's bass is not one of its tones"
|
|
666
369
|
);
|
|
667
370
|
}
|
|
668
371
|
const written = /* @__PURE__ */ new Map();
|
|
669
372
|
for (const tone of tones) {
|
|
670
373
|
if (tone !== bass) {
|
|
671
|
-
written.set(
|
|
374
|
+
written.set(_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, tone.letter - bass.letter) + 1, tone);
|
|
672
375
|
}
|
|
673
376
|
}
|
|
674
377
|
const digits = FIGURE_ABBREVIATIONS.get([...written.keys()].sort((a, b) => a - b).join(","));
|
|
675
378
|
if (digits === void 0) {
|
|
676
|
-
throw new (0,
|
|
677
|
-
`no figured bass names ${
|
|
379
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
380
|
+
`no figured bass names ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkYPB3A3EYcjs.noteNames.call(void 0, [bass])[0]}`
|
|
678
381
|
);
|
|
679
382
|
}
|
|
680
383
|
const accidentals = /* @__PURE__ */ new Map();
|
|
681
384
|
for (const [number, tone] of written) {
|
|
682
|
-
|
|
683
|
-
if (alteration === 0) {
|
|
385
|
+
if (tone.alter === (_nullishCoalesce(alters[_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, tone.letter)], () => ( 0)))) {
|
|
684
386
|
continue;
|
|
685
387
|
}
|
|
686
|
-
const glyph = ACCIDENTAL_GLYPHS
|
|
388
|
+
const glyph = ACCIDENTAL_GLYPHS.get(tone.alter);
|
|
687
389
|
if (glyph === void 0) {
|
|
688
|
-
throw new (0,
|
|
689
|
-
`no figure names an interval altered by ${alteration} semitones from the key`
|
|
690
|
-
);
|
|
390
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(`no accidental writes a note altered by ${tone.alter} semitones`);
|
|
691
391
|
}
|
|
692
392
|
accidentals.set(number, glyph);
|
|
693
393
|
}
|
|
694
394
|
const figures = [.../* @__PURE__ */ new Set([...digits, ...accidentals.keys()])].sort((a, b) => b - a).map((number) => `${_nullishCoalesce(accidentals.get(number), () => ( ""))}${number}`).join("");
|
|
695
|
-
assertRealizesAs(bass, figures,
|
|
395
|
+
assertRealizesAs(bass, figures, resolved, tones);
|
|
696
396
|
return figures;
|
|
697
397
|
}
|
|
698
398
|
function pitchClassesOf(notes) {
|
|
699
|
-
return [...new Set(notes.map(
|
|
399
|
+
return [...new Set(notes.map(_chunkXKSO2V5Icjs.noteToPitchClass))].sort((a, b) => a - b);
|
|
700
400
|
}
|
|
701
401
|
function assertRealizesAs(bass, figures, key, tones) {
|
|
702
|
-
const noFigures = (detail) => new (0,
|
|
703
|
-
`no figured bass names ${
|
|
402
|
+
const noFigures = (detail) => new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
403
|
+
`no figured bass names ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkYPB3A3EYcjs.noteNames.call(void 0, [bass])[0]}: the figures ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, figures)} ${detail}`
|
|
704
404
|
);
|
|
705
405
|
let realized;
|
|
706
406
|
try {
|
|
707
407
|
realized = figuredBassRealization(bass, figures, key);
|
|
708
408
|
} catch (error) {
|
|
709
|
-
throw error instanceof
|
|
409
|
+
throw error instanceof _chunkXKSO2V5Icjs.NoSolutionError ? noFigures("read back as no chord at all") : error;
|
|
710
410
|
}
|
|
711
411
|
const written = pitchClassesOf(realized.notes);
|
|
712
412
|
const expected = pitchClassesOf(tones);
|
|
713
413
|
if (written.length !== expected.length || written.some((pc, index) => pc !== expected[index])) {
|
|
714
|
-
throw noFigures(`sound ${
|
|
414
|
+
throw noFigures(`sound ${_chunkYPB3A3EYcjs.noteNames.call(void 0, realized.notes).join(" ")} instead`);
|
|
715
415
|
}
|
|
716
416
|
}
|
|
717
417
|
|
|
718
|
-
// src/theory/
|
|
719
|
-
function
|
|
720
|
-
|
|
418
|
+
// src/theory/partwriting/cross-relation.ts
|
|
419
|
+
function resolvesAsAppliedDominant(chord, next, key) {
|
|
420
|
+
if (next === void 0 || !_chunkYPB3A3EYcjs.hasDominantSonority.call(void 0, chord)) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
const target = _chunkYPB3A3EYcjs.appliedDominantTarget.call(void 0, chord.rootPc + 5, key);
|
|
424
|
+
return target !== null && _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, next.rootPc) === target.rootPc;
|
|
721
425
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc) === _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc + 7);
|
|
426
|
+
function soundsAugmentedSixth(moment, key) {
|
|
427
|
+
const bass = moment.notes[0];
|
|
428
|
+
if (bass === void 0 || _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bass) !== _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc + 8)) {
|
|
429
|
+
return false;
|
|
727
430
|
}
|
|
728
|
-
|
|
729
|
-
|
|
431
|
+
return moment.notes.some((note, voice) => {
|
|
432
|
+
if (voice === 0) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, bass, note);
|
|
436
|
+
return (interval.number - 1) % 7 + 1 === 6 && _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, interval.semitones) === 10;
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
function isChromaticHarmony(moment, next, key) {
|
|
440
|
+
return resolvesAsAppliedDominant(moment.chord, next, key) || _chunkYPB3A3EYcjs.isNeapolitanChordOf.call(void 0, moment.chord, key) || soundsAugmentedSixth(moment, key);
|
|
441
|
+
}
|
|
442
|
+
function approachedByStep(before, moment, voice) {
|
|
443
|
+
const earlier = _optionalChain([before, 'optionalAccess', _2 => _2[voice]]);
|
|
444
|
+
const later = moment[voice];
|
|
445
|
+
if (earlier === void 0 || later === void 0) {
|
|
446
|
+
return false;
|
|
730
447
|
}
|
|
731
|
-
return
|
|
448
|
+
return _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later).number === 2;
|
|
732
449
|
}
|
|
733
|
-
function
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
450
|
+
function isOuterVoice(voice, notes) {
|
|
451
|
+
return voice === 0 || voice === notes.length - 1;
|
|
452
|
+
}
|
|
453
|
+
function crossRelations(from, to, key, context = {}) {
|
|
454
|
+
const found = [];
|
|
455
|
+
const chromatic = isChromaticHarmony(from, to.chord, key) || isChromaticHarmony(to, context.after, key);
|
|
456
|
+
if (chromatic) {
|
|
457
|
+
return found;
|
|
458
|
+
}
|
|
459
|
+
const reported = /* @__PURE__ */ new Set();
|
|
460
|
+
for (let first = 0; first < from.notes.length; first += 1) {
|
|
461
|
+
for (let second = 0; second < to.notes.length; second += 1) {
|
|
462
|
+
if (first === second) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const earlier = from.notes[first];
|
|
466
|
+
const later = to.notes[second];
|
|
467
|
+
if (earlier === void 0 || later === void 0) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const letter = _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, earlier.letter);
|
|
471
|
+
if (letter !== _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, later.letter) || earlier.alter === later.alter) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (approachedByStep(context.before, from.notes, first) || approachedByStep(from.notes, to.notes, second) || !isOuterVoice(first, from.notes) && !isOuterVoice(second, to.notes)) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const pair = `${letter}:${Math.min(first, second)}:${Math.max(first, second)}`;
|
|
478
|
+
if (reported.has(pair)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
reported.add(pair);
|
|
482
|
+
found.push({ earlierVoice: first, laterVoice: second, earlier, later });
|
|
738
483
|
}
|
|
739
484
|
}
|
|
740
|
-
return
|
|
485
|
+
return found;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/theory/voicing/tendency.ts
|
|
489
|
+
function spellingTable(keyLike, chord) {
|
|
490
|
+
const { tonic, scale: key } = _chunkYPB3A3EYcjs.resolveKey.call(void 0, keyLike);
|
|
491
|
+
const notes = [];
|
|
492
|
+
const bases = [];
|
|
493
|
+
for (let pc = 0; pc < 12; pc += 1) {
|
|
494
|
+
const note = chord === void 0 ? _chunkYPB3A3EYcjs.spellPitchClass.call(void 0, pc, tonic, key) : _chunkYPB3A3EYcjs.spellPitchClass.call(void 0, pc, tonic, key, { chord });
|
|
495
|
+
notes.push(note);
|
|
496
|
+
bases.push(_chunkXKSO2V5Icjs.noteToMidi.call(void 0, { letter: note.letter, alter: note.alter, octave: 0 }));
|
|
497
|
+
}
|
|
498
|
+
return { notes, bases };
|
|
499
|
+
}
|
|
500
|
+
function spelledAt(table, pitch) {
|
|
501
|
+
const pc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch);
|
|
502
|
+
const note = table.notes[pc];
|
|
503
|
+
const base = table.bases[pc];
|
|
504
|
+
if (note === void 0 || base === void 0) {
|
|
505
|
+
return void 0;
|
|
506
|
+
}
|
|
507
|
+
return { letter: note.letter, alter: note.alter, octave: (pitch - base) / 12 };
|
|
508
|
+
}
|
|
509
|
+
function movesByAugmentedInterval(from, fromPitch, to, toPitch) {
|
|
510
|
+
const earlier = spelledAt(from, fromPitch);
|
|
511
|
+
const later = spelledAt(to, toPitch);
|
|
512
|
+
if (earlier === void 0 || later === void 0) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
return _chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, earlier, later);
|
|
516
|
+
}
|
|
517
|
+
function movesByDescendingStep(from, fromPitch, to, toPitch) {
|
|
518
|
+
const earlier = spelledAt(from, fromPitch);
|
|
519
|
+
const later = spelledAt(to, toPitch);
|
|
520
|
+
if (earlier === void 0 || later === void 0) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
return _chunkYPB3A3EYcjs.isDescendingStep.call(void 0, _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later));
|
|
741
524
|
}
|
|
742
525
|
|
|
743
526
|
// src/theory/voicing/internal.ts
|
|
@@ -750,8 +533,11 @@ var UNISON_PENALTY = 100;
|
|
|
750
533
|
var LEADING_TONE_DOUBLING_PENALTY = 200;
|
|
751
534
|
var RESOLUTION_PENALTY = 200;
|
|
752
535
|
var DEFAULT_MAX_CANDIDATES = 4e3;
|
|
536
|
+
var MAX_LOOKAHEAD_PAIRS = 1e6;
|
|
753
537
|
var SEARCH_NODES_PER_CANDIDATE = 16;
|
|
754
|
-
var HIDDEN_PERFECT_PENALTY =
|
|
538
|
+
var HIDDEN_PERFECT_PENALTY = 100;
|
|
539
|
+
var CROSS_RELATION_PENALTY = HIDDEN_PERFECT_PENALTY;
|
|
540
|
+
var AUGMENTED_MELODIC_PENALTY = VIOLATION_PENALTY;
|
|
755
541
|
var INITIAL_CANDIDATE_CAPACITY = 512;
|
|
756
542
|
function createCandidateBuffer() {
|
|
757
543
|
return { pitches: new Int32Array(0), count: 0, voices: 0 };
|
|
@@ -759,7 +545,7 @@ function createCandidateBuffer() {
|
|
|
759
545
|
function pitchesForPc(pc, range) {
|
|
760
546
|
const result = [];
|
|
761
547
|
for (let midi = Math.ceil(range.min); midi <= range.max; midi += 1) {
|
|
762
|
-
if (
|
|
548
|
+
if (_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, midi) === pc) {
|
|
763
549
|
result.push(midi);
|
|
764
550
|
}
|
|
765
551
|
}
|
|
@@ -771,14 +557,14 @@ function pitchesForPc(pc, range) {
|
|
|
771
557
|
});
|
|
772
558
|
}
|
|
773
559
|
function enumerateVoicings(chord, ranges, maxSpacing, maxCandidates = DEFAULT_MAX_CANDIDATES, into = createCandidateBuffer()) {
|
|
774
|
-
const chordPcs =
|
|
775
|
-
const bassPc =
|
|
560
|
+
const chordPcs = _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, chord);
|
|
561
|
+
const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
|
|
776
562
|
const byVoice = ranges.map(
|
|
777
563
|
(range, voice) => (voice === 0 ? [bassPc] : chordPcs).flatMap((pc) => pitchesForPc(pc, range))
|
|
778
564
|
);
|
|
779
565
|
for (let voice = 0; voice < byVoice.length; voice += 1) {
|
|
780
566
|
if ((_nullishCoalesce(byVoice[voice], () => ( []))).length === 0) {
|
|
781
|
-
throw new (0,
|
|
567
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)("no voicing satisfies the given ranges");
|
|
782
568
|
}
|
|
783
569
|
}
|
|
784
570
|
const voices = ranges.length;
|
|
@@ -827,17 +613,17 @@ function enumerateVoicings(chord, ranges, maxSpacing, maxCandidates = DEFAULT_MA
|
|
|
827
613
|
return into;
|
|
828
614
|
}
|
|
829
615
|
function structuralTables(chord, key) {
|
|
830
|
-
const tones =
|
|
616
|
+
const tones = _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, chord);
|
|
831
617
|
const missing = tones.map(
|
|
832
|
-
(pc) =>
|
|
618
|
+
(pc) => _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord) === "fifth" ? MISSING_FIFTH_PENALTY : MISSING_TONE_PENALTY
|
|
833
619
|
);
|
|
834
|
-
const rootPc =
|
|
835
|
-
const leadingTonePc = key !== void 0 && isFunctioningLeadingTone(chord, key) ? leadingTonePcOf(key) : -1;
|
|
620
|
+
const rootPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
621
|
+
const leadingTonePc = key !== void 0 && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, chord, key) ? _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key) : -1;
|
|
836
622
|
const doubling = new Float64Array(12);
|
|
837
623
|
for (let pc = 0; pc < 12; pc += 1) {
|
|
838
624
|
if (pc === leadingTonePc) {
|
|
839
625
|
doubling[pc] = LEADING_TONE_DOUBLING_PENALTY;
|
|
840
|
-
} else if (pc !== rootPc &&
|
|
626
|
+
} else if (pc !== rootPc && _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord) !== "fifth") {
|
|
841
627
|
doubling[pc] = POOR_DOUBLING_PENALTY;
|
|
842
628
|
}
|
|
843
629
|
}
|
|
@@ -852,7 +638,7 @@ function structuralPenalty(tables, pitches, offset, voices) {
|
|
|
852
638
|
if (pitch === void 0) {
|
|
853
639
|
continue;
|
|
854
640
|
}
|
|
855
|
-
const pc =
|
|
641
|
+
const pc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch);
|
|
856
642
|
PITCH_CLASS_COUNTS[pc] = (_nullishCoalesce(PITCH_CLASS_COUNTS[pc], () => ( 0))) + 1;
|
|
857
643
|
if (index > 0 && pitch === pitches[offset + index - 1]) {
|
|
858
644
|
penalty += UNISON_PENALTY;
|
|
@@ -874,18 +660,19 @@ function structuralPenalty(tables, pitches, offset, voices) {
|
|
|
874
660
|
}
|
|
875
661
|
function resolutionTables(prevChord, nextChord, key) {
|
|
876
662
|
const nextHas = new Uint8Array(12);
|
|
877
|
-
for (const pc of
|
|
663
|
+
for (const pc of _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, nextChord)) {
|
|
878
664
|
nextHas[pc] = 1;
|
|
879
665
|
}
|
|
880
666
|
return {
|
|
881
|
-
seventhPc: _nullishCoalesce(seventhPcOf(prevChord), () => ( -1)),
|
|
882
|
-
leadingTonePc: key !== void 0 && isFunctioningLeadingTone(prevChord, key) ? leadingTonePcOf(key) : -1,
|
|
883
|
-
tonicPc: key === void 0 ? -1 :
|
|
884
|
-
nextHas
|
|
667
|
+
seventhPc: _nullishCoalesce(_chunkYPB3A3EYcjs.seventhPcOf.call(void 0, prevChord), () => ( -1)),
|
|
668
|
+
leadingTonePc: key !== void 0 && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, prevChord, key) ? _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key) : -1,
|
|
669
|
+
tonicPc: key === void 0 ? -1 : _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc),
|
|
670
|
+
nextHas,
|
|
671
|
+
nextChord
|
|
885
672
|
};
|
|
886
673
|
}
|
|
887
|
-
function resolutionViolations(tables, prev, prevOffset, cur, curOffset, voices) {
|
|
888
|
-
const { seventhPc, leadingTonePc, tonicPc, nextHas } = tables;
|
|
674
|
+
function resolutionViolations(tables, spelling, prev, prevOffset, cur, curOffset, voices) {
|
|
675
|
+
const { seventhPc, leadingTonePc, tonicPc, nextHas, nextChord } = tables;
|
|
889
676
|
let count = 0;
|
|
890
677
|
for (let voice = 0; voice < voices; voice += 1) {
|
|
891
678
|
const from = prev[prevOffset + voice];
|
|
@@ -893,12 +680,14 @@ function resolutionViolations(tables, prev, prevOffset, cur, curOffset, voices)
|
|
|
893
680
|
if (from === void 0 || to === void 0) {
|
|
894
681
|
continue;
|
|
895
682
|
}
|
|
896
|
-
const fromPc =
|
|
683
|
+
const fromPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, from);
|
|
897
684
|
const motion = to - from;
|
|
898
|
-
|
|
685
|
+
const resolved = spelling === void 0 ? motion === -1 || motion === -2 : movesByDescendingStep(spelling.from, from, spelling.to, to);
|
|
686
|
+
if (fromPc === seventhPc && nextHas[fromPc] === 0 && !resolved) {
|
|
899
687
|
count += 1;
|
|
900
688
|
}
|
|
901
|
-
|
|
689
|
+
const inner = voice > 0 && voice < voices - 1;
|
|
690
|
+
if (fromPc === leadingTonePc && tonicPc >= 0 && nextHas[tonicPc] === 1 && nextHas[fromPc] === 0 && motion !== 1 && !(inner && _chunkYPB3A3EYcjs.isFrustratedLeadingTone.call(void 0, from, to, nextChord))) {
|
|
902
691
|
count += 1;
|
|
903
692
|
}
|
|
904
693
|
}
|
|
@@ -914,16 +703,76 @@ function leadingCost(prev, prevOffset, cur, curOffset, voices) {
|
|
|
914
703
|
}
|
|
915
704
|
total += Math.abs(b - a);
|
|
916
705
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
706
|
+
return total;
|
|
707
|
+
}
|
|
708
|
+
function createsOuterHiddenPerfect(prev, prevOffset, cur, curOffset, voices) {
|
|
709
|
+
if (voices < 2) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
const bassPrev = prev[prevOffset];
|
|
713
|
+
const bassCur = cur[curOffset];
|
|
714
|
+
const sopPrev = prev[prevOffset + voices - 1];
|
|
715
|
+
const sopCur = cur[curOffset + voices - 1];
|
|
716
|
+
if (bassPrev === void 0 || bassCur === void 0 || sopPrev === void 0 || sopCur === void 0) {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
return _chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, sopPrev, sopCur, bassPrev, bassCur);
|
|
720
|
+
}
|
|
721
|
+
function augmentedMelodicCount(spelling, prev, prevOffset, cur, curOffset, voices) {
|
|
722
|
+
let count = 0;
|
|
723
|
+
for (let voice = 0; voice < voices; voice += 1) {
|
|
724
|
+
const from = prev[prevOffset + voice];
|
|
725
|
+
const to = cur[curOffset + voice];
|
|
726
|
+
if (from === void 0 || to === void 0) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (movesByAugmentedInterval(spelling.from, from, spelling.to, to)) {
|
|
730
|
+
count += 1;
|
|
924
731
|
}
|
|
925
732
|
}
|
|
926
|
-
return
|
|
733
|
+
return count;
|
|
734
|
+
}
|
|
735
|
+
function spellRow(table, pitches, offset, voices) {
|
|
736
|
+
const notes = [];
|
|
737
|
+
for (let voice = 0; voice < voices; voice += 1) {
|
|
738
|
+
const pitch = pitches[offset + voice];
|
|
739
|
+
const note = pitch === void 0 ? void 0 : spelledAt(table, pitch);
|
|
740
|
+
if (note !== void 0) {
|
|
741
|
+
notes.push(note);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return notes;
|
|
745
|
+
}
|
|
746
|
+
function contradictsALetter(spelling, prev, prevOffset, cur, curOffset, voices) {
|
|
747
|
+
for (let voice = 0; voice < voices; voice += 1) {
|
|
748
|
+
const from = prev[prevOffset + voice];
|
|
749
|
+
const earlier = from === void 0 ? void 0 : spelling.from.notes[_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, from)];
|
|
750
|
+
if (earlier === void 0) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
for (let other = 0; other < voices; other += 1) {
|
|
754
|
+
if (other === voice) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
const to = cur[curOffset + other];
|
|
758
|
+
const later = to === void 0 ? void 0 : spelling.to.notes[_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, to)];
|
|
759
|
+
if (later !== void 0 && earlier.alter !== later.alter && _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, earlier.letter) === _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, later.letter)) {
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
function crossRelationCount(spelling, tables, prev, prevOffset, cur, curOffset, voices) {
|
|
767
|
+
if (!contradictsALetter(spelling, prev, prevOffset, cur, curOffset, voices)) {
|
|
768
|
+
return 0;
|
|
769
|
+
}
|
|
770
|
+
return crossRelations(
|
|
771
|
+
{ notes: spellRow(spelling.from, prev, prevOffset, voices), chord: tables.fromChord },
|
|
772
|
+
{ notes: spellRow(spelling.to, cur, curOffset, voices), chord: tables.toChord },
|
|
773
|
+
tables.key,
|
|
774
|
+
{ after: tables.afterChord }
|
|
775
|
+
).length;
|
|
927
776
|
}
|
|
928
777
|
function violationCount(prev, prevOffset, cur, curOffset, voices) {
|
|
929
778
|
let count = 0;
|
|
@@ -936,11 +785,11 @@ function violationCount(prev, prevOffset, cur, curOffset, voices) {
|
|
|
936
785
|
if (prevLower === void 0 || prevUpper === void 0 || curLower === void 0 || curUpper === void 0) {
|
|
937
786
|
continue;
|
|
938
787
|
}
|
|
939
|
-
if (
|
|
788
|
+
if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
|
|
940
789
|
count += 1;
|
|
941
790
|
}
|
|
942
791
|
if (upper === lower + 1) {
|
|
943
|
-
if (
|
|
792
|
+
if (_chunkYPB3A3EYcjs.createsVoiceOverlap.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
|
|
944
793
|
count += 1;
|
|
945
794
|
}
|
|
946
795
|
}
|
|
@@ -948,6 +797,24 @@ function violationCount(prev, prevOffset, cur, curOffset, voices) {
|
|
|
948
797
|
}
|
|
949
798
|
return count;
|
|
950
799
|
}
|
|
800
|
+
function moveScoring(prevChord, chord, key, nextChord) {
|
|
801
|
+
return {
|
|
802
|
+
structure: structuralTables(chord, _optionalChain([key, 'optionalAccess', _3 => _3.scale])),
|
|
803
|
+
resolution: prevChord === void 0 ? void 0 : resolutionTables(prevChord, chord, _optionalChain([key, 'optionalAccess', _4 => _4.scale])),
|
|
804
|
+
spelling: key === void 0 ? void 0 : { from: spellingTable(key, prevChord), to: spellingTable(key, chord) },
|
|
805
|
+
crossRelation: prevChord === void 0 || key === void 0 ? void 0 : { fromChord: prevChord, toChord: chord, afterChord: nextChord, key: key.scale }
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
function violationWeight(scoring, prev, prevOffset, cur, curOffset, voices) {
|
|
809
|
+
const { resolution, spelling, crossRelation } = scoring;
|
|
810
|
+
const unresolved = resolution === void 0 ? 0 : resolutionViolations(resolution, spelling, prev, prevOffset, cur, curOffset, voices);
|
|
811
|
+
const augmented = spelling === void 0 ? 0 : augmentedMelodicCount(spelling, prev, prevOffset, cur, curOffset, voices);
|
|
812
|
+
const contradictions = spelling === void 0 || crossRelation === void 0 ? 0 : crossRelationCount(spelling, crossRelation, prev, prevOffset, cur, curOffset, voices);
|
|
813
|
+
return VIOLATION_PENALTY * violationCount(prev, prevOffset, cur, curOffset, voices) + RESOLUTION_PENALTY * unresolved + AUGMENTED_MELODIC_PENALTY * augmented + CROSS_RELATION_PENALTY * contradictions + (createsOuterHiddenPerfect(prev, prevOffset, cur, curOffset, voices) ? HIDDEN_PERFECT_PENALTY : 0);
|
|
814
|
+
}
|
|
815
|
+
function moveScore(scoring, prev, prevOffset, cur, curOffset, voices) {
|
|
816
|
+
return structuralPenalty(scoring.structure, cur, curOffset, voices) + leadingCost(prev, prevOffset, cur, curOffset, voices) + violationWeight(scoring, prev, prevOffset, cur, curOffset, voices);
|
|
817
|
+
}
|
|
951
818
|
|
|
952
819
|
// src/theory/voicing/satb.ts
|
|
953
820
|
var SATB_RANGES = Object.freeze([
|
|
@@ -960,18 +827,19 @@ var DERIVED_LOW = 40;
|
|
|
960
827
|
var DERIVED_HIGH = 79;
|
|
961
828
|
var DERIVED_SPAN = 19;
|
|
962
829
|
function resolveRanges(opts) {
|
|
963
|
-
|
|
830
|
+
_chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
|
|
831
|
+
if (_optionalChain([opts, 'optionalAccess', _5 => _5.ranges]) !== void 0) {
|
|
964
832
|
if (opts.ranges.length === 0) {
|
|
965
|
-
throw new (0,
|
|
833
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("ranges must contain at least one voice range");
|
|
966
834
|
}
|
|
967
|
-
|
|
835
|
+
_chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, opts.ranges.length, "voice ranges", 128);
|
|
968
836
|
return opts.ranges.map((range, index) => {
|
|
969
|
-
|
|
970
|
-
|
|
837
|
+
_chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, range.min, `ranges[${index}].min`);
|
|
838
|
+
_chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, range.max, `ranges[${index}].max`);
|
|
971
839
|
if (range.min > range.max) {
|
|
972
|
-
throw new (0,
|
|
840
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`ranges[${index}].min must not exceed max`);
|
|
973
841
|
}
|
|
974
|
-
|
|
842
|
+
_chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
|
|
975
843
|
Math.floor(range.max) - Math.ceil(range.min) + 1,
|
|
976
844
|
`ranges[${index}] span`,
|
|
977
845
|
4096
|
|
@@ -979,8 +847,8 @@ function resolveRanges(opts) {
|
|
|
979
847
|
return { ...range };
|
|
980
848
|
});
|
|
981
849
|
}
|
|
982
|
-
const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
983
|
-
|
|
850
|
+
const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _6 => _6.voices]), () => ( 4));
|
|
851
|
+
_chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, voices, "voices", 128);
|
|
984
852
|
if (voices === 4) {
|
|
985
853
|
return SATB_RANGES.map((range) => ({ ...range }));
|
|
986
854
|
}
|
|
@@ -997,59 +865,63 @@ function resolveRanges(opts) {
|
|
|
997
865
|
return ranges;
|
|
998
866
|
}
|
|
999
867
|
function resolveMaxSpacing(opts) {
|
|
1000
|
-
const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
1001
|
-
|
|
868
|
+
const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _7 => _7.maxSpacing]), () => ( DEFAULT_MAX_SPACING));
|
|
869
|
+
_chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, maxSpacing, "maxSpacing");
|
|
1002
870
|
if (maxSpacing < 0) {
|
|
1003
|
-
throw new (0,
|
|
871
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("maxSpacing must be non-negative");
|
|
1004
872
|
}
|
|
1005
873
|
return maxSpacing;
|
|
1006
874
|
}
|
|
1007
|
-
function
|
|
1008
|
-
return _optionalChain([opts, 'optionalAccess',
|
|
875
|
+
function resolvedKeyOf(opts) {
|
|
876
|
+
return _optionalChain([opts, 'optionalAccess', _8 => _8.key]) === void 0 ? void 0 : _chunkYPB3A3EYcjs.resolveKey.call(void 0, opts.key);
|
|
1009
877
|
}
|
|
1010
878
|
function resolvePreviousChord(opts) {
|
|
1011
|
-
return _optionalChain([opts, 'optionalAccess',
|
|
879
|
+
return _optionalChain([opts, 'optionalAccess', _9 => _9.previousChord]) === void 0 ? void 0 : _chunkYPB3A3EYcjs.toChordData.call(void 0, opts.previousChord);
|
|
1012
880
|
}
|
|
1013
881
|
function resolveMaxCandidates(opts) {
|
|
1014
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1015
|
-
|
|
882
|
+
if (_optionalChain([opts, 'optionalAccess', _10 => _10.maxCandidates]) === void 0) return void 0;
|
|
883
|
+
_chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, opts.maxCandidates, "maxCandidates", 1e6);
|
|
1016
884
|
return opts.maxCandidates;
|
|
1017
885
|
}
|
|
886
|
+
function centeringPenalty(ranges, pitches, offset, voices) {
|
|
887
|
+
let penalty = 0;
|
|
888
|
+
for (let voice = 0; voice < voices; voice += 1) {
|
|
889
|
+
const pitch = pitches[offset + voice];
|
|
890
|
+
const range = ranges[voice];
|
|
891
|
+
if (pitch === void 0 || range === void 0) {
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
penalty += Math.abs(pitch - (range.min + range.max) / 2);
|
|
895
|
+
}
|
|
896
|
+
return penalty;
|
|
897
|
+
}
|
|
1018
898
|
function voiceChord(chord, opts) {
|
|
1019
|
-
const data =
|
|
899
|
+
const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
|
|
1020
900
|
const ranges = resolveRanges(opts);
|
|
1021
901
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
1022
902
|
const candidates = enumerateVoicings(data, ranges, maxSpacing, resolveMaxCandidates(opts));
|
|
1023
|
-
const
|
|
903
|
+
const { structure } = moveScoring(void 0, data, resolvedKeyOf(opts));
|
|
1024
904
|
const { pitches, voices } = candidates;
|
|
1025
905
|
let bestOffset = -1;
|
|
1026
906
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
1027
907
|
for (let index = 0; index < candidates.count; index += 1) {
|
|
1028
908
|
const offset = index * voices;
|
|
1029
|
-
|
|
1030
|
-
for (let i = 0; i < voices; i += 1) {
|
|
1031
|
-
const pitch = pitches[offset + i];
|
|
1032
|
-
const range = ranges[i];
|
|
1033
|
-
if (pitch === void 0 || range === void 0) {
|
|
1034
|
-
continue;
|
|
1035
|
-
}
|
|
1036
|
-
score += Math.abs(pitch - (range.min + range.max) / 2);
|
|
1037
|
-
}
|
|
909
|
+
const score = structuralPenalty(structure, pitches, offset, voices) + centeringPenalty(ranges, pitches, offset, voices);
|
|
1038
910
|
if (score < bestScore) {
|
|
1039
911
|
bestScore = score;
|
|
1040
912
|
bestOffset = offset;
|
|
1041
913
|
}
|
|
1042
914
|
}
|
|
1043
915
|
if (bestOffset < 0) {
|
|
1044
|
-
throw new (0,
|
|
1045
|
-
`no voicing satisfies the given ranges for ${
|
|
916
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
917
|
+
`no voicing satisfies the given ranges for ${_chunkYPB3A3EYcjs.formatChordSymbol.call(void 0, chord)}`
|
|
1046
918
|
);
|
|
1047
919
|
}
|
|
1048
920
|
return [...pitches.subarray(bestOffset, bestOffset + voices)];
|
|
1049
921
|
}
|
|
1050
922
|
function noSolutionAt(index, chord) {
|
|
1051
|
-
return new (0,
|
|
1052
|
-
`no voicing satisfies the given ranges for ${
|
|
923
|
+
return new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
|
|
924
|
+
`no voicing satisfies the given ranges for ${_chunkYPB3A3EYcjs.formatChordSymbol.call(void 0, chord)} at index ${index}`,
|
|
1053
925
|
{ at: index }
|
|
1054
926
|
);
|
|
1055
927
|
}
|
|
@@ -1057,48 +929,72 @@ function locate(index, chord, work) {
|
|
|
1057
929
|
try {
|
|
1058
930
|
return work();
|
|
1059
931
|
} catch (error) {
|
|
1060
|
-
if (error instanceof
|
|
932
|
+
if (error instanceof _chunkXKSO2V5Icjs.NoSolutionError) {
|
|
1061
933
|
throw noSolutionAt(index, chord);
|
|
1062
934
|
}
|
|
1063
935
|
throw error;
|
|
1064
936
|
}
|
|
1065
937
|
}
|
|
938
|
+
function successorWeight(next, cur, curOffset, voices) {
|
|
939
|
+
const { candidates, scoring } = next;
|
|
940
|
+
const { pitches } = candidates;
|
|
941
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
942
|
+
let bestWeight = 0;
|
|
943
|
+
for (let candidate = 0; candidate < candidates.count; candidate += 1) {
|
|
944
|
+
const offset = candidate * voices;
|
|
945
|
+
const weight = violationWeight(scoring, cur, curOffset, pitches, offset, voices);
|
|
946
|
+
const score = weight + structuralPenalty(scoring.structure, pitches, offset, voices) + leadingCost(cur, curOffset, pitches, offset, voices);
|
|
947
|
+
if (score < bestScore) {
|
|
948
|
+
bestScore = score;
|
|
949
|
+
bestWeight = weight;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return bestWeight;
|
|
953
|
+
}
|
|
1066
954
|
function voiceProgression(chords, opts) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
const
|
|
955
|
+
const progression = _chunkXKSO2V5Icjs.assertArray.call(void 0, chords, "chords");
|
|
956
|
+
_chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, progression.length, "voiced progression chords", _optionalChain([opts, 'optionalAccess', _11 => _11.budget]));
|
|
957
|
+
const data = progression.map((chord) => _chunkYPB3A3EYcjs.toChordData.call(void 0, chord));
|
|
958
|
+
const key = resolvedKeyOf(opts);
|
|
1070
959
|
const ranges = resolveRanges(opts);
|
|
1071
960
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
1072
961
|
const maxCandidates = resolveMaxCandidates(opts);
|
|
1073
962
|
const result = [];
|
|
1074
|
-
const
|
|
963
|
+
const first = data[0];
|
|
964
|
+
if (first === void 0) {
|
|
965
|
+
return result;
|
|
966
|
+
}
|
|
967
|
+
let current = locate(
|
|
968
|
+
0,
|
|
969
|
+
first,
|
|
970
|
+
() => enumerateVoicings(first, ranges, maxSpacing, maxCandidates, createCandidateBuffer())
|
|
971
|
+
);
|
|
972
|
+
let spare = createCandidateBuffer();
|
|
973
|
+
let scoring = moveScoring(void 0, first, key);
|
|
1075
974
|
let prev;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
() => enumerateVoicings(chord, ranges, maxSpacing, maxCandidates, buffer)
|
|
1092
|
-
);
|
|
1093
|
-
const { pitches, voices } = candidates;
|
|
1094
|
-
const structure = structuralTables(chord, key);
|
|
1095
|
-
const resolution = prevChord === void 0 ? void 0 : resolutionTables(prevChord, chord, key);
|
|
975
|
+
for (const [index, chord] of data.entries()) {
|
|
976
|
+
const nextChord = data[index + 1];
|
|
977
|
+
const lookahead = nextChord === void 0 ? void 0 : {
|
|
978
|
+
candidates: locate(
|
|
979
|
+
index + 1,
|
|
980
|
+
nextChord,
|
|
981
|
+
() => enumerateVoicings(nextChord, ranges, maxSpacing, maxCandidates, spare)
|
|
982
|
+
),
|
|
983
|
+
// The chord after the one being reached goes in too: it is what
|
|
984
|
+
// says whether the chromatic tone the next chord introduces is the
|
|
985
|
+
// one an applied dominant is written with.
|
|
986
|
+
scoring: moveScoring(chord, nextChord, key, data[index + 2])
|
|
987
|
+
};
|
|
988
|
+
const { pitches, voices, count } = current;
|
|
989
|
+
const weighed = lookahead !== void 0 && count * lookahead.candidates.count <= MAX_LOOKAHEAD_PAIRS ? lookahead : void 0;
|
|
1096
990
|
let bestOffset = -1;
|
|
1097
991
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
1098
|
-
for (let candidate = 0; candidate <
|
|
992
|
+
for (let candidate = 0; candidate < count; candidate += 1) {
|
|
1099
993
|
const offset = candidate * voices;
|
|
1100
|
-
|
|
1101
|
-
|
|
994
|
+
let score = prev === void 0 ? structuralPenalty(scoring.structure, pitches, offset, voices) + centeringPenalty(ranges, pitches, offset, voices) : moveScore(scoring, prev, 0, pitches, offset, voices);
|
|
995
|
+
if (weighed !== void 0) {
|
|
996
|
+
score += successorWeight(weighed, pitches, offset, voices);
|
|
997
|
+
}
|
|
1102
998
|
if (score < bestScore) {
|
|
1103
999
|
bestScore = score;
|
|
1104
1000
|
bestOffset = offset;
|
|
@@ -1110,28 +1006,41 @@ function voiceProgression(chords, opts) {
|
|
|
1110
1006
|
const best = [...pitches.subarray(bestOffset, bestOffset + voices)];
|
|
1111
1007
|
result.push(best);
|
|
1112
1008
|
prev = best;
|
|
1113
|
-
|
|
1009
|
+
if (lookahead !== void 0) {
|
|
1010
|
+
spare = current;
|
|
1011
|
+
current = lookahead.candidates;
|
|
1012
|
+
scoring = lookahead.scoring;
|
|
1013
|
+
}
|
|
1114
1014
|
}
|
|
1115
1015
|
return result;
|
|
1116
1016
|
}
|
|
1117
1017
|
|
|
1118
1018
|
// src/theory/voicing/leading.ts
|
|
1019
|
+
function assertVoicing(pitches, name) {
|
|
1020
|
+
const voices = _chunkXKSO2V5Icjs.assertArray.call(void 0, pitches, name);
|
|
1021
|
+
for (let index = 0; index < voices.length; index += 1) {
|
|
1022
|
+
_chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, _nullishCoalesce(voices[index], () => ( Number.NaN)), `${name}[${index}]`);
|
|
1023
|
+
}
|
|
1024
|
+
return voices;
|
|
1025
|
+
}
|
|
1119
1026
|
function voiceLeadingCost(from, to) {
|
|
1120
|
-
|
|
1027
|
+
const previous = assertVoicing(from, "from");
|
|
1028
|
+
const next = assertVoicing(to, "to");
|
|
1029
|
+
if (previous.length !== next.length) {
|
|
1121
1030
|
return Number.POSITIVE_INFINITY;
|
|
1122
1031
|
}
|
|
1123
|
-
return leadingCost(
|
|
1032
|
+
return leadingCost(previous, 0, next, 0, previous.length);
|
|
1124
1033
|
}
|
|
1125
1034
|
function nextVoicing(current, chord, opts) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1035
|
+
_chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
|
|
1036
|
+
assertVoicing(current, "current");
|
|
1037
|
+
const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
|
|
1038
|
+
const derived = _optionalChain([opts, 'optionalAccess', _12 => _12.ranges]) === void 0 && _optionalChain([opts, 'optionalAccess', _13 => _13.voices]) === void 0;
|
|
1128
1039
|
if (derived) {
|
|
1129
1040
|
if (current.length === 0) {
|
|
1130
|
-
throw new (0,
|
|
1131
|
-
}
|
|
1132
|
-
for (let index = 0; index < current.length; index += 1) {
|
|
1133
|
-
_chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
|
|
1041
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("current must contain at least one pitch");
|
|
1134
1042
|
}
|
|
1043
|
+
assertVoicing(current, "current");
|
|
1135
1044
|
}
|
|
1136
1045
|
const ranges = derived ? resolveRanges({
|
|
1137
1046
|
ranges: current.map((pitch) => {
|
|
@@ -1139,11 +1048,9 @@ function nextVoicing(current, chord, opts) {
|
|
|
1139
1048
|
return { min: Math.max(0, centre - 12), max: Math.min(127, centre + 12) };
|
|
1140
1049
|
})
|
|
1141
1050
|
}) : resolveRanges(opts);
|
|
1142
|
-
|
|
1143
|
-
_chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
|
|
1144
|
-
}
|
|
1051
|
+
assertVoicing(current, "current");
|
|
1145
1052
|
if (current.length < ranges.length) {
|
|
1146
|
-
throw new (0,
|
|
1053
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
1147
1054
|
`current has ${current.length} voices but the requested ranges require ${ranges.length}`
|
|
1148
1055
|
);
|
|
1149
1056
|
}
|
|
@@ -1153,29 +1060,33 @@ function nextVoicing(current, chord, opts) {
|
|
|
1153
1060
|
});
|
|
1154
1061
|
const maxSpacing = resolveMaxSpacing(opts);
|
|
1155
1062
|
const candidates = enumerateVoicings(data, ranges, maxSpacing, resolveMaxCandidates(opts));
|
|
1156
|
-
const key =
|
|
1157
|
-
const
|
|
1158
|
-
const structure = structuralTables(data, key);
|
|
1159
|
-
const resolution = previousChord === void 0 ? void 0 : resolutionTables(previousChord, data, key);
|
|
1063
|
+
const key = resolvedKeyOf(opts);
|
|
1064
|
+
const scoring = moveScoring(resolvePreviousChord(opts), data, key);
|
|
1160
1065
|
const { pitches, voices } = candidates;
|
|
1161
1066
|
let bestOffset = -1;
|
|
1162
1067
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
1163
1068
|
for (let candidate = 0; candidate < candidates.count; candidate += 1) {
|
|
1164
1069
|
const offset = candidate * voices;
|
|
1165
|
-
const score =
|
|
1070
|
+
const score = moveScore(scoring, source, 0, pitches, offset, voices);
|
|
1166
1071
|
if (score < bestScore) {
|
|
1167
1072
|
bestScore = score;
|
|
1168
1073
|
bestOffset = offset;
|
|
1169
1074
|
}
|
|
1170
1075
|
}
|
|
1171
1076
|
if (bestOffset < 0) {
|
|
1172
|
-
throw new (0,
|
|
1077
|
+
throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)("no voicing satisfies the given ranges");
|
|
1173
1078
|
}
|
|
1174
1079
|
return [...pitches.subarray(bestOffset, bestOffset + voices)];
|
|
1175
1080
|
}
|
|
1176
1081
|
|
|
1177
1082
|
// src/theory/voicing/styled.ts
|
|
1178
1083
|
var DEFAULT_STYLE_OCTAVE = 4;
|
|
1084
|
+
function dropPositionFromTop(style) {
|
|
1085
|
+
if (style === "drop2") {
|
|
1086
|
+
return 2;
|
|
1087
|
+
}
|
|
1088
|
+
return style === "drop3" ? 3 : 0;
|
|
1089
|
+
}
|
|
1179
1090
|
function dedupePcs(pcs) {
|
|
1180
1091
|
const seen = /* @__PURE__ */ new Set();
|
|
1181
1092
|
const result = [];
|
|
@@ -1188,7 +1099,7 @@ function dedupePcs(pcs) {
|
|
|
1188
1099
|
return result;
|
|
1189
1100
|
}
|
|
1190
1101
|
function lowestPitchAtOrAbove(pc, floor) {
|
|
1191
|
-
return floor + ((pc -
|
|
1102
|
+
return floor + ((pc - _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, floor)) % 12 + 12) % 12;
|
|
1192
1103
|
}
|
|
1193
1104
|
function nearestPc(target, pcs) {
|
|
1194
1105
|
let best = _nullishCoalesce(pcs[0], () => ( target));
|
|
@@ -1202,26 +1113,29 @@ function nearestPc(target, pcs) {
|
|
|
1202
1113
|
}
|
|
1203
1114
|
return best;
|
|
1204
1115
|
}
|
|
1205
|
-
function voiceChordStyled(
|
|
1206
|
-
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1116
|
+
function voiceChordStyled(given, opts) {
|
|
1117
|
+
_chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
|
|
1118
|
+
const chord = _chunkYPB3A3EYcjs.toChordData.call(void 0, given);
|
|
1119
|
+
const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _14 => _14.style]), () => ( "close"));
|
|
1120
|
+
const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _15 => _15.octave]), () => ( DEFAULT_STYLE_OCTAVE));
|
|
1121
|
+
_chunkXKSO2V5Icjs.assertInteger.call(void 0, octave, "styled voicing octave", -10, 10);
|
|
1122
|
+
if (_optionalChain([opts, 'optionalAccess', _16 => _16.topNote]) !== void 0) {
|
|
1123
|
+
_chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, opts.topNote, "styled voicing topNote");
|
|
1211
1124
|
}
|
|
1212
1125
|
const base = 12 * (octave + 1);
|
|
1213
|
-
const rootPc =
|
|
1214
|
-
const bassPc =
|
|
1215
|
-
const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess',
|
|
1216
|
-
let order = dedupePcs(chord.intervals.map((interval) =>
|
|
1126
|
+
const rootPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc);
|
|
1127
|
+
const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
|
|
1128
|
+
const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess', _17 => _17.rootless]) === true;
|
|
1129
|
+
let order = dedupePcs(chord.intervals.map((interval) => _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc + interval)));
|
|
1217
1130
|
const bassIndex = order.indexOf(bassPc);
|
|
1131
|
+
const bassIsChordTone = bassIndex >= 0;
|
|
1218
1132
|
if (bassIndex > 0) {
|
|
1219
1133
|
order = [...order.slice(bassIndex), ...order.slice(0, bassIndex)];
|
|
1220
1134
|
} else if (bassIndex < 0) {
|
|
1221
1135
|
order = [bassPc, ...order];
|
|
1222
1136
|
}
|
|
1223
1137
|
if (style === "shell") {
|
|
1224
|
-
const roles = order.map((pc) =>
|
|
1138
|
+
const roles = order.map((pc) => _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord));
|
|
1225
1139
|
const guide = roles.includes("seventh") ? "seventh" : roles.includes("sixth") ? "sixth" : "fifth";
|
|
1226
1140
|
order = order.filter((_pc, i) => {
|
|
1227
1141
|
const role = roles[i];
|
|
@@ -1234,18 +1148,27 @@ function voiceChordStyled(chord, opts) {
|
|
|
1234
1148
|
if (order.length === 0) {
|
|
1235
1149
|
return [];
|
|
1236
1150
|
}
|
|
1237
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1238
|
-
const target = order.includes(
|
|
1151
|
+
if (_optionalChain([opts, 'optionalAccess', _18 => _18.topNote]) !== void 0) {
|
|
1152
|
+
const target = order.includes(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote)) ? _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote) : nearestPc(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote), order);
|
|
1239
1153
|
const targetIndex = order.indexOf(target);
|
|
1240
1154
|
order = [...order.slice(targetIndex + 1), ...order.slice(0, targetIndex + 1)];
|
|
1241
1155
|
}
|
|
1156
|
+
const dropFromTop = dropPositionFromTop(style);
|
|
1157
|
+
const dropsToNamedBass = dropFromTop > 0 && bassIsChordTone && chord.bassPc !== void 0 && order.length > dropFromTop;
|
|
1158
|
+
if (dropsToNamedBass) {
|
|
1159
|
+
const bassAt = order.indexOf(bassPc);
|
|
1160
|
+
if (bassAt >= 0) {
|
|
1161
|
+
const shift = ((bassAt - (order.length - dropFromTop)) % order.length + order.length) % order.length;
|
|
1162
|
+
order = [...order.slice(shift), ...order.slice(0, shift)];
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1242
1165
|
const stack = [];
|
|
1243
1166
|
let prev;
|
|
1244
1167
|
for (const pc of order) {
|
|
1245
1168
|
if (prev === void 0) {
|
|
1246
1169
|
prev = lowestPitchAtOrAbove(pc, base);
|
|
1247
1170
|
} else {
|
|
1248
|
-
let delta = ((pc -
|
|
1171
|
+
let delta = ((pc - _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, prev)) % 12 + 12) % 12;
|
|
1249
1172
|
if (delta === 0) {
|
|
1250
1173
|
delta = 12;
|
|
1251
1174
|
}
|
|
@@ -1253,13 +1176,13 @@ function voiceChordStyled(chord, opts) {
|
|
|
1253
1176
|
}
|
|
1254
1177
|
stack.push(prev);
|
|
1255
1178
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
stack[
|
|
1179
|
+
const stackedFloor = bassIsChordTone ? 0 : 1;
|
|
1180
|
+
if (dropFromTop > 0 && stack.length - dropFromTop > stackedFloor) {
|
|
1181
|
+
const dropIndex = stack.length - dropFromTop;
|
|
1182
|
+
stack[dropIndex] = (_nullishCoalesce(stack[dropIndex], () => ( 0))) - 12;
|
|
1260
1183
|
}
|
|
1261
1184
|
if (chord.bassPc !== void 0) {
|
|
1262
|
-
const bassIndexInStack = stack.findIndex((pitch) =>
|
|
1185
|
+
const bassIndexInStack = stack.findIndex((pitch) => _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch) === bassPc);
|
|
1263
1186
|
if (bassIndexInStack >= 0) {
|
|
1264
1187
|
const otherLowest = Math.min(
|
|
1265
1188
|
...stack.filter((_pitch, index) => index !== bassIndexInStack),
|
|
@@ -1274,7 +1197,7 @@ function voiceChordStyled(chord, opts) {
|
|
|
1274
1197
|
const lowest = _nullishCoalesce(voicing[0], () => ( 0));
|
|
1275
1198
|
const highest = _nullishCoalesce(voicing[voicing.length - 1], () => ( 0));
|
|
1276
1199
|
if (lowest < 0 || highest > 127) {
|
|
1277
|
-
throw new (0,
|
|
1200
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
1278
1201
|
`styled voicing at octave ${octave} spans MIDI ${lowest}..${highest}, outside 0..127`
|
|
1279
1202
|
);
|
|
1280
1203
|
}
|
|
@@ -1290,17 +1213,26 @@ var INTERVAL_WORDS = [
|
|
|
1290
1213
|
"fifth",
|
|
1291
1214
|
"sixth",
|
|
1292
1215
|
"seventh",
|
|
1293
|
-
"octave"
|
|
1216
|
+
"octave",
|
|
1217
|
+
"ninth",
|
|
1218
|
+
"tenth",
|
|
1219
|
+
"eleventh",
|
|
1220
|
+
"twelfth",
|
|
1221
|
+
"thirteenth",
|
|
1222
|
+
"fourteenth",
|
|
1223
|
+
"fifteenth"
|
|
1294
1224
|
];
|
|
1295
1225
|
function intervalWord(numberValue) {
|
|
1296
|
-
return _nullishCoalesce(INTERVAL_WORDS[numberValue - 1], () => (
|
|
1226
|
+
return _nullishCoalesce(INTERVAL_WORDS[numberValue - 1], () => ( `${numberValue}th`));
|
|
1227
|
+
}
|
|
1228
|
+
function intervalArticle(word) {
|
|
1229
|
+
return /^[aeiou]/.test(word) ? "an" : "a";
|
|
1297
1230
|
}
|
|
1298
1231
|
function violation(kind, voices, fromIndex, toIndex, rationale) {
|
|
1299
1232
|
return { kind, voices, fromIndex, toIndex, rationale };
|
|
1300
1233
|
}
|
|
1301
1234
|
|
|
1302
1235
|
// src/theory/partwriting/species.ts
|
|
1303
|
-
var EPS = 1e-9;
|
|
1304
1236
|
var SPECIES_RATIO = { 1: 1, 2: 2, 3: 4, 4: 2, 5: 0 };
|
|
1305
1237
|
var SPECIES_NUMBERS = Object.keys(SPECIES_RATIO).map(Number);
|
|
1306
1238
|
var MIN_FLORID_VALUE = 0.125;
|
|
@@ -1308,7 +1240,7 @@ function meanPitch(notes) {
|
|
|
1308
1240
|
if (notes.length === 0) {
|
|
1309
1241
|
return 0;
|
|
1310
1242
|
}
|
|
1311
|
-
return notes.reduce((sum, note) => sum +
|
|
1243
|
+
return notes.reduce((sum, note) => sum + _chunkXKSO2V5Icjs.noteToMidi.call(void 0, note), 0) / notes.length;
|
|
1312
1244
|
}
|
|
1313
1245
|
function impliedDurations(count, measures, ratio) {
|
|
1314
1246
|
const full = ratio * Math.max(measures - 1, 0);
|
|
@@ -1324,21 +1256,22 @@ function direction(interval) {
|
|
|
1324
1256
|
function sameNote(a, b) {
|
|
1325
1257
|
return a.letter === b.letter && a.alter === b.alter && a.octave === b.octave;
|
|
1326
1258
|
}
|
|
1327
|
-
function isPassedThrough(entries, position) {
|
|
1259
|
+
function isPassedThrough(entries, position, species) {
|
|
1328
1260
|
const previous = entries[position - 1];
|
|
1329
1261
|
const current = entries[position];
|
|
1330
1262
|
const next = entries[position + 1];
|
|
1331
1263
|
if (previous === void 0 || current === void 0 || next === void 0) {
|
|
1332
1264
|
return false;
|
|
1333
1265
|
}
|
|
1334
|
-
const incoming =
|
|
1335
|
-
const outgoing =
|
|
1266
|
+
const incoming = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, previous.note, current.note);
|
|
1267
|
+
const outgoing = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, current.note, next.note);
|
|
1336
1268
|
if (!isStep(incoming) || !isStep(outgoing)) {
|
|
1337
1269
|
return false;
|
|
1338
1270
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1271
|
+
if (direction(incoming) === direction(outgoing)) {
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
return sameNote(previous.note, next.note) && (species === 3 || species === 5);
|
|
1342
1275
|
}
|
|
1343
1276
|
function isThird(interval) {
|
|
1344
1277
|
return interval.number === 3;
|
|
@@ -1362,9 +1295,9 @@ function isCambiata(entries, start) {
|
|
|
1362
1295
|
if (first.consonance === 2 /* Dissonance */ || third.consonance === 2 /* Dissonance */) {
|
|
1363
1296
|
return false;
|
|
1364
1297
|
}
|
|
1365
|
-
const approach =
|
|
1366
|
-
const leap =
|
|
1367
|
-
const answer =
|
|
1298
|
+
const approach = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.note, second.note);
|
|
1299
|
+
const leap = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, second.note, third.note);
|
|
1300
|
+
const answer = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, third.note, fourth.note);
|
|
1368
1301
|
return isStep(approach) && direction(approach) < 0 && isThird(leap) && direction(leap) < 0 && isStep(answer) && direction(answer) > 0;
|
|
1369
1302
|
}
|
|
1370
1303
|
function isDoubleNeighbour(entries, start) {
|
|
@@ -1376,9 +1309,9 @@ function isDoubleNeighbour(entries, start) {
|
|
|
1376
1309
|
if (first.consonance === 2 /* Dissonance */ || !sameNote(first.note, fourth.note)) {
|
|
1377
1310
|
return false;
|
|
1378
1311
|
}
|
|
1379
|
-
const away =
|
|
1380
|
-
const across =
|
|
1381
|
-
const back =
|
|
1312
|
+
const away = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.note, second.note);
|
|
1313
|
+
const across = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, second.note, third.note);
|
|
1314
|
+
const back = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, third.note, fourth.note);
|
|
1382
1315
|
return isStep(away) && isThird(across) && isStep(back) && direction(away) !== 0 && direction(across) === -direction(away);
|
|
1383
1316
|
}
|
|
1384
1317
|
function isFiguredDissonance(entries, position, species) {
|
|
@@ -1399,7 +1332,7 @@ function licenceFor(species, downbeat) {
|
|
|
1399
1332
|
}
|
|
1400
1333
|
return downbeat ? "none" : "passing";
|
|
1401
1334
|
}
|
|
1402
|
-
function verticalViolations(entries, position, species, counterpointAbove) {
|
|
1335
|
+
function verticalViolations(entries, position, species, counterpointAbove, crossed) {
|
|
1403
1336
|
const entry = entries[position];
|
|
1404
1337
|
if (entry === void 0) {
|
|
1405
1338
|
return [];
|
|
@@ -1407,7 +1340,8 @@ function verticalViolations(entries, position, species, counterpointAbove) {
|
|
|
1407
1340
|
const found = [];
|
|
1408
1341
|
const upper = counterpointAbove ? entry.note : entry.against;
|
|
1409
1342
|
const lower = counterpointAbove ? entry.against : entry.note;
|
|
1410
|
-
if (
|
|
1343
|
+
if (!crossed.has(entry.index) && _chunkYPB3A3EYcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
|
|
1344
|
+
crossed.add(entry.index);
|
|
1411
1345
|
found.push(
|
|
1412
1346
|
violation(
|
|
1413
1347
|
"voiceCrossing",
|
|
@@ -1422,7 +1356,7 @@ function verticalViolations(entries, position, species, counterpointAbove) {
|
|
|
1422
1356
|
return found;
|
|
1423
1357
|
}
|
|
1424
1358
|
const licence = licenceFor(species, entry.downbeat);
|
|
1425
|
-
if (licence === "passing" && (isPassedThrough(entries, position) || isFiguredDissonance(entries, position, species))) {
|
|
1359
|
+
if (licence === "passing" && (isPassedThrough(entries, position, species) || isFiguredDissonance(entries, position, species))) {
|
|
1426
1360
|
return found;
|
|
1427
1361
|
}
|
|
1428
1362
|
if (licence === "suspension") {
|
|
@@ -1459,7 +1393,7 @@ function suspensionViolations(entries, position) {
|
|
|
1459
1393
|
)
|
|
1460
1394
|
];
|
|
1461
1395
|
}
|
|
1462
|
-
const resolution = next === void 0 ? void 0 :
|
|
1396
|
+
const resolution = next === void 0 ? void 0 : _chunkXKSO2V5Icjs.spelledInterval.call(void 0, entry.note, next.note);
|
|
1463
1397
|
if (next === void 0 || resolution === void 0 || !isStep(resolution) || direction(resolution) >= 0) {
|
|
1464
1398
|
return [
|
|
1465
1399
|
violation(
|
|
@@ -1471,11 +1405,22 @@ function suspensionViolations(entries, position) {
|
|
|
1471
1405
|
)
|
|
1472
1406
|
];
|
|
1473
1407
|
}
|
|
1408
|
+
if (next.consonance === 2 /* Dissonance */) {
|
|
1409
|
+
return [
|
|
1410
|
+
violation(
|
|
1411
|
+
"unresolvedSuspension",
|
|
1412
|
+
[0, 1],
|
|
1413
|
+
entry.index,
|
|
1414
|
+
next.index,
|
|
1415
|
+
`The suspension falls onto a ${next.interval.quality}${next.interval.number}, which is no resolution`
|
|
1416
|
+
)
|
|
1417
|
+
];
|
|
1418
|
+
}
|
|
1474
1419
|
return [];
|
|
1475
1420
|
}
|
|
1476
1421
|
function melodicViolations(from, to) {
|
|
1477
|
-
const interval =
|
|
1478
|
-
if (
|
|
1422
|
+
const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, from.note, to.note);
|
|
1423
|
+
if (_chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, from.note, to.note)) {
|
|
1479
1424
|
return [
|
|
1480
1425
|
violation(
|
|
1481
1426
|
"augmentedMelodicInterval",
|
|
@@ -1486,7 +1431,7 @@ function melodicViolations(from, to) {
|
|
|
1486
1431
|
)
|
|
1487
1432
|
];
|
|
1488
1433
|
}
|
|
1489
|
-
if (
|
|
1434
|
+
if (_chunkYPB3A3EYcjs.isForbiddenMelodicLeap.call(void 0, from.note, to.note)) {
|
|
1490
1435
|
return [
|
|
1491
1436
|
violation(
|
|
1492
1437
|
"illegalLeap",
|
|
@@ -1514,19 +1459,19 @@ function melodicMoves(entries) {
|
|
|
1514
1459
|
if (from === void 0 || to === void 0) {
|
|
1515
1460
|
continue;
|
|
1516
1461
|
}
|
|
1517
|
-
moves.push({ from, to, interval:
|
|
1462
|
+
moves.push({ from, to, interval: _chunkXKSO2V5Icjs.spelledInterval.call(void 0, from.note, to.note) });
|
|
1518
1463
|
}
|
|
1519
1464
|
return moves;
|
|
1520
1465
|
}
|
|
1521
1466
|
function climaxViolations(entries) {
|
|
1522
1467
|
const body = entries.slice(0, -1);
|
|
1523
1468
|
const peak = body.reduce(
|
|
1524
|
-
(high, entry) => Math.max(high,
|
|
1469
|
+
(high, entry) => Math.max(high, _chunkXKSO2V5Icjs.noteToMidi.call(void 0, entry.note)),
|
|
1525
1470
|
Number.NEGATIVE_INFINITY
|
|
1526
1471
|
);
|
|
1527
1472
|
const arrivals = body.filter((entry, position) => {
|
|
1528
1473
|
const previous = body[position - 1];
|
|
1529
|
-
return
|
|
1474
|
+
return _chunkXKSO2V5Icjs.noteToMidi.call(void 0, entry.note) === peak && (previous === void 0 || _chunkXKSO2V5Icjs.noteToMidi.call(void 0, previous.note) !== peak);
|
|
1530
1475
|
});
|
|
1531
1476
|
const first = arrivals[0];
|
|
1532
1477
|
const second = arrivals[1];
|
|
@@ -1607,7 +1552,7 @@ function leapAnswerViolations(moves) {
|
|
|
1607
1552
|
[1],
|
|
1608
1553
|
leap.from.index,
|
|
1609
1554
|
answer.to.index,
|
|
1610
|
-
`The leap of ${word
|
|
1555
|
+
`The leap of ${intervalArticle(word)} ${word} is not answered by a step the other way`
|
|
1611
1556
|
)
|
|
1612
1557
|
);
|
|
1613
1558
|
}
|
|
@@ -1620,7 +1565,7 @@ function tritoneOutlineViolations(entries) {
|
|
|
1620
1565
|
const first = run[0];
|
|
1621
1566
|
const last = run[run.length - 1];
|
|
1622
1567
|
if (first !== void 0 && last !== void 0 && run.length > 1) {
|
|
1623
|
-
const outline =
|
|
1568
|
+
const outline = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.from.note, last.to.note);
|
|
1624
1569
|
if (Math.abs(outline.semitones) === TRITONE_SEMITONES) {
|
|
1625
1570
|
found.push(
|
|
1626
1571
|
violation(
|
|
@@ -1663,7 +1608,7 @@ function repeatedNoteViolations(moves, closingMeasure) {
|
|
|
1663
1608
|
}
|
|
1664
1609
|
function melodicShapeViolations(entries, species) {
|
|
1665
1610
|
const moves = melodicMoves(entries);
|
|
1666
|
-
const closingMeasure = _nullishCoalesce(_optionalChain([entries, 'access',
|
|
1611
|
+
const closingMeasure = _nullishCoalesce(_optionalChain([entries, 'access', _19 => _19[entries.length - 1], 'optionalAccess', _20 => _20.againstIndex]), () => ( 0));
|
|
1667
1612
|
const found = [
|
|
1668
1613
|
...climaxViolations(entries),
|
|
1669
1614
|
...leapChainViolations(moves),
|
|
@@ -1673,14 +1618,19 @@ function melodicShapeViolations(entries, species) {
|
|
|
1673
1618
|
];
|
|
1674
1619
|
return found.sort((left, right) => left.fromIndex - right.fromIndex);
|
|
1675
1620
|
}
|
|
1621
|
+
function inTimeOrder(violations) {
|
|
1622
|
+
return [...violations].sort(
|
|
1623
|
+
(left, right) => left.fromIndex - right.fromIndex || left.toIndex - right.toIndex
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1676
1626
|
function motionViolations(from, to, counterpointAbove) {
|
|
1677
1627
|
const found = [];
|
|
1678
1628
|
const upperPrev = counterpointAbove ? from.note : from.against;
|
|
1679
1629
|
const upperCur = counterpointAbove ? to.note : to.against;
|
|
1680
1630
|
const lowerPrev = counterpointAbove ? from.against : from.note;
|
|
1681
1631
|
const lowerCur = counterpointAbove ? to.against : to.note;
|
|
1682
|
-
if (
|
|
1683
|
-
const fifth =
|
|
1632
|
+
if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
|
|
1633
|
+
const fifth = _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, to.interval.number) === 5;
|
|
1684
1634
|
found.push(
|
|
1685
1635
|
violation(
|
|
1686
1636
|
fifth ? "parallelFifth" : "parallelOctave",
|
|
@@ -1691,18 +1641,18 @@ function motionViolations(from, to, counterpointAbove) {
|
|
|
1691
1641
|
)
|
|
1692
1642
|
);
|
|
1693
1643
|
}
|
|
1694
|
-
if (
|
|
1644
|
+
if (_chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur, "twoVoice")) {
|
|
1695
1645
|
found.push(
|
|
1696
1646
|
violation(
|
|
1697
1647
|
"hiddenPerfect",
|
|
1698
1648
|
[0, 1],
|
|
1699
1649
|
from.index,
|
|
1700
1650
|
to.index,
|
|
1701
|
-
"The voices
|
|
1651
|
+
"The voices move into a perfect interval by similar motion"
|
|
1702
1652
|
)
|
|
1703
1653
|
);
|
|
1704
1654
|
}
|
|
1705
|
-
if (
|
|
1655
|
+
if (_chunkYPB3A3EYcjs.createsBattuta.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
|
|
1706
1656
|
found.push(
|
|
1707
1657
|
violation(
|
|
1708
1658
|
"battuta",
|
|
@@ -1731,7 +1681,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
|
|
|
1731
1681
|
if (first === void 0 || last === void 0) {
|
|
1732
1682
|
return found;
|
|
1733
1683
|
}
|
|
1734
|
-
if (
|
|
1684
|
+
if (_chunkYPB3A3EYcjs.classifySpelledInterval.call(void 0, first.interval) !== 0 /* PerfectConsonance */) {
|
|
1735
1685
|
found.push(
|
|
1736
1686
|
violation(
|
|
1737
1687
|
"missingCadence",
|
|
@@ -1742,7 +1692,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
|
|
|
1742
1692
|
)
|
|
1743
1693
|
);
|
|
1744
1694
|
}
|
|
1745
|
-
if (
|
|
1695
|
+
if (_chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, last.note) !== _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, mode.rootPc)) {
|
|
1746
1696
|
found.push(
|
|
1747
1697
|
violation(
|
|
1748
1698
|
"missingCadence",
|
|
@@ -1754,7 +1704,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
|
|
|
1754
1704
|
);
|
|
1755
1705
|
}
|
|
1756
1706
|
const closing = last.interval;
|
|
1757
|
-
if (closing.quality !== "P" ||
|
|
1707
|
+
if (closing.quality !== "P" || _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, closing.number) !== 1) {
|
|
1758
1708
|
found.push(
|
|
1759
1709
|
violation(
|
|
1760
1710
|
"missingCadence",
|
|
@@ -1771,9 +1721,9 @@ function cadenceViolations(entries, mode, counterpointAbove) {
|
|
|
1771
1721
|
return found;
|
|
1772
1722
|
}
|
|
1773
1723
|
const arrival = _nullishCoalesce(entries.find((entry) => entry.againstIndex === last.againstIndex), () => ( last));
|
|
1774
|
-
const approach =
|
|
1724
|
+
const approach = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, penultimate.note, arrival.note);
|
|
1775
1725
|
const expected = counterpointAbove ? { quality: "M", number: 6 } : { quality: "m", number: 3 };
|
|
1776
|
-
if (penultimate.interval.quality !== expected.quality ||
|
|
1726
|
+
if (penultimate.interval.quality !== expected.quality || _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, penultimate.interval.number) !== expected.number || !isStep(approach)) {
|
|
1777
1727
|
found.push(
|
|
1778
1728
|
violation(
|
|
1779
1729
|
"missingCadence",
|
|
@@ -1787,7 +1737,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
|
|
|
1787
1737
|
return found;
|
|
1788
1738
|
}
|
|
1789
1739
|
function resolveDurations(counterpoint, measures, species, opts) {
|
|
1790
|
-
const given = _optionalChain([opts, 'optionalAccess',
|
|
1740
|
+
const given = _optionalChain([opts, 'optionalAccess', _21 => _21.durations]);
|
|
1791
1741
|
if (given !== void 0) {
|
|
1792
1742
|
if (given.length !== counterpoint.length) {
|
|
1793
1743
|
return {
|
|
@@ -1796,20 +1746,20 @@ function resolveDurations(counterpoint, measures, species, opts) {
|
|
|
1796
1746
|
}
|
|
1797
1747
|
for (const duration of given) {
|
|
1798
1748
|
const units = duration / MIN_FLORID_VALUE;
|
|
1799
|
-
if (!(duration > 0) || duration > 1 || Math.abs(units - Math.round(units)) >
|
|
1749
|
+
if (!(duration > 0) || duration > 1 || Math.abs(units - Math.round(units)) > _chunkXKSO2V5Icjs.BEAT_EPS) {
|
|
1800
1750
|
return {
|
|
1801
1751
|
problem: `A note lasts ${duration} of a measure, which this style does not write`
|
|
1802
1752
|
};
|
|
1803
1753
|
}
|
|
1804
1754
|
}
|
|
1805
1755
|
const total = given.reduce((sum, duration) => sum + duration, 0);
|
|
1806
|
-
if (Math.abs(total - measures) >
|
|
1756
|
+
if (Math.abs(total - measures) > _chunkXKSO2V5Icjs.BEAT_EPS) {
|
|
1807
1757
|
return { problem: `The counterpoint fills ${total} of the ${measures} measures` };
|
|
1808
1758
|
}
|
|
1809
1759
|
return { durations: [...given] };
|
|
1810
1760
|
}
|
|
1811
1761
|
if (species === 5) {
|
|
1812
|
-
throw new (0,
|
|
1762
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
1813
1763
|
"checkSpecies needs durations for the fifth species, whose note values are not fixed"
|
|
1814
1764
|
);
|
|
1815
1765
|
}
|
|
@@ -1831,23 +1781,32 @@ function buildEntries(cantusFirmus, counterpoint, durations, counterpointAbove)
|
|
|
1831
1781
|
if (note === void 0) {
|
|
1832
1782
|
continue;
|
|
1833
1783
|
}
|
|
1834
|
-
const
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1784
|
+
const struckOver = Math.min(Math.floor(onset + _chunkXKSO2V5Icjs.BEAT_EPS), cantusFirmus.length - 1);
|
|
1785
|
+
const lastOver = Math.min(
|
|
1786
|
+
Math.max(Math.ceil(onset + duration - _chunkXKSO2V5Icjs.BEAT_EPS) - 1, struckOver),
|
|
1787
|
+
cantusFirmus.length - 1
|
|
1788
|
+
);
|
|
1789
|
+
for (let againstIndex = struckOver; againstIndex <= lastOver; againstIndex += 1) {
|
|
1790
|
+
const against = cantusFirmus[againstIndex];
|
|
1791
|
+
if (against === void 0) {
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
const held = againstIndex > struckOver;
|
|
1795
|
+
const begins = held ? againstIndex : onset;
|
|
1796
|
+
const interval = counterpointAbove ? _chunkXKSO2V5Icjs.spelledInterval.call(void 0, against, note) : _chunkXKSO2V5Icjs.spelledInterval.call(void 0, note, against);
|
|
1797
|
+
entries.push({
|
|
1798
|
+
note,
|
|
1799
|
+
index,
|
|
1800
|
+
onset: begins,
|
|
1801
|
+
duration: onset + duration - begins,
|
|
1802
|
+
held,
|
|
1803
|
+
against,
|
|
1804
|
+
againstIndex,
|
|
1805
|
+
downbeat: Math.abs(begins - Math.round(begins)) < _chunkXKSO2V5Icjs.BEAT_EPS,
|
|
1806
|
+
interval,
|
|
1807
|
+
consonance: _chunkYPB3A3EYcjs.classifySpelledInterval.call(void 0, interval)
|
|
1808
|
+
});
|
|
1838
1809
|
}
|
|
1839
|
-
const interval = counterpointAbove ? _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, against, note) : _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, note, against);
|
|
1840
|
-
entries.push({
|
|
1841
|
-
note,
|
|
1842
|
-
index,
|
|
1843
|
-
onset,
|
|
1844
|
-
duration,
|
|
1845
|
-
against,
|
|
1846
|
-
againstIndex,
|
|
1847
|
-
downbeat: Math.abs(onset - Math.round(onset)) < EPS,
|
|
1848
|
-
interval,
|
|
1849
|
-
consonance: _chunkUADYAYATcjs.classifySpelledInterval.call(void 0, interval)
|
|
1850
|
-
});
|
|
1851
1810
|
onset += duration;
|
|
1852
1811
|
}
|
|
1853
1812
|
return entries;
|
|
@@ -1866,40 +1825,50 @@ function motionPairs(entries) {
|
|
|
1866
1825
|
}
|
|
1867
1826
|
return pairs;
|
|
1868
1827
|
}
|
|
1869
|
-
function checkSpecies(cantusFirmus, counterpoint, species,
|
|
1828
|
+
function checkSpecies(cantusFirmus, counterpoint, species, modeLike, opts) {
|
|
1829
|
+
_chunkXKSO2V5Icjs.assertArray.call(void 0, cantusFirmus, "cantus firmus");
|
|
1830
|
+
_chunkXKSO2V5Icjs.assertArray.call(void 0, counterpoint, "counterpoint");
|
|
1831
|
+
const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
|
|
1832
|
+
const mode = _chunkYPB3A3EYcjs.toKeyScale.call(void 0, modeLike);
|
|
1870
1833
|
if (!SPECIES_NUMBERS.includes(species)) {
|
|
1871
|
-
throw new (0,
|
|
1872
|
-
`species must be one of ${SPECIES_NUMBERS.join(", ")}; received ${
|
|
1834
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
1835
|
+
`species must be one of ${SPECIES_NUMBERS.join(", ")}; received ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, species)}`
|
|
1873
1836
|
);
|
|
1874
1837
|
}
|
|
1875
1838
|
if (cantusFirmus.length === 0 || counterpoint.length === 0) {
|
|
1876
|
-
throw new (0,
|
|
1839
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("checkSpecies needs both a cantus firmus and a counterpoint");
|
|
1877
1840
|
}
|
|
1878
|
-
const resolved = resolveDurations(counterpoint, cantusFirmus.length, species,
|
|
1841
|
+
const resolved = resolveDurations(counterpoint, cantusFirmus.length, species, asked);
|
|
1879
1842
|
if ("problem" in resolved) {
|
|
1880
|
-
return
|
|
1843
|
+
return inTimeOrder([
|
|
1844
|
+
violation("wrongRhythmicRatio", [1], 0, counterpoint.length - 1, resolved.problem)
|
|
1845
|
+
]);
|
|
1881
1846
|
}
|
|
1882
|
-
const counterpointAbove = _nullishCoalesce(
|
|
1847
|
+
const counterpointAbove = _nullishCoalesce(asked.counterpointAbove, () => ( meanPitch(counterpoint) >= meanPitch(cantusFirmus)));
|
|
1883
1848
|
const entries = buildEntries(cantusFirmus, counterpoint, resolved.durations, counterpointAbove);
|
|
1849
|
+
const struck = entries.filter((entry) => !entry.held);
|
|
1884
1850
|
const violations = [];
|
|
1851
|
+
const crossed = /* @__PURE__ */ new Set();
|
|
1885
1852
|
for (let position = 0; position < entries.length; position += 1) {
|
|
1886
|
-
violations.push(...verticalViolations(entries, position, species, counterpointAbove));
|
|
1887
|
-
|
|
1888
|
-
|
|
1853
|
+
violations.push(...verticalViolations(entries, position, species, counterpointAbove, crossed));
|
|
1854
|
+
}
|
|
1855
|
+
for (let position = 1; position < struck.length; position += 1) {
|
|
1856
|
+
const previous = struck[position - 1];
|
|
1857
|
+
const current = struck[position];
|
|
1889
1858
|
if (previous !== void 0 && current !== void 0) {
|
|
1890
1859
|
violations.push(...melodicViolations(previous, current));
|
|
1891
1860
|
}
|
|
1892
1861
|
}
|
|
1893
|
-
violations.push(...melodicShapeViolations(
|
|
1894
|
-
for (const [from, to] of motionPairs(
|
|
1895
|
-
const previous =
|
|
1896
|
-
const current =
|
|
1862
|
+
violations.push(...melodicShapeViolations(struck, species));
|
|
1863
|
+
for (const [from, to] of motionPairs(struck)) {
|
|
1864
|
+
const previous = struck[from];
|
|
1865
|
+
const current = struck[to];
|
|
1897
1866
|
if (previous !== void 0 && current !== void 0) {
|
|
1898
1867
|
violations.push(...motionViolations(previous, current, counterpointAbove));
|
|
1899
1868
|
}
|
|
1900
1869
|
}
|
|
1901
|
-
violations.push(...cadenceViolations(
|
|
1902
|
-
return violations;
|
|
1870
|
+
violations.push(...cadenceViolations(struck, mode, counterpointAbove));
|
|
1871
|
+
return inTimeOrder(violations);
|
|
1903
1872
|
}
|
|
1904
1873
|
|
|
1905
1874
|
// src/theory/partwriting/index.ts
|
|
@@ -1908,12 +1877,9 @@ function bare(note) {
|
|
|
1908
1877
|
}
|
|
1909
1878
|
function midiOf(note, index, voice) {
|
|
1910
1879
|
if (note.octave === void 0) {
|
|
1911
|
-
throw new (0,
|
|
1880
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`voicings[${index}][${voice}] must carry an octave`);
|
|
1912
1881
|
}
|
|
1913
|
-
return
|
|
1914
|
-
}
|
|
1915
|
-
function isDescendingStep(interval) {
|
|
1916
|
-
return interval.number === 2 && (interval.semitones === -1 || interval.semitones === -2);
|
|
1882
|
+
return _chunkXKSO2V5Icjs.noteToMidi.call(void 0, note);
|
|
1917
1883
|
}
|
|
1918
1884
|
function verticalViolations2(moment, ranges, maxSpacing) {
|
|
1919
1885
|
const found = [];
|
|
@@ -1924,7 +1890,7 @@ function verticalViolations2(moment, ranges, maxSpacing) {
|
|
|
1924
1890
|
if (upper === void 0 || lower === void 0) {
|
|
1925
1891
|
continue;
|
|
1926
1892
|
}
|
|
1927
|
-
if (
|
|
1893
|
+
if (_chunkYPB3A3EYcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
|
|
1928
1894
|
found.push(
|
|
1929
1895
|
violation(
|
|
1930
1896
|
"voiceCrossing",
|
|
@@ -1935,7 +1901,7 @@ function verticalViolations2(moment, ranges, maxSpacing) {
|
|
|
1935
1901
|
)
|
|
1936
1902
|
);
|
|
1937
1903
|
}
|
|
1938
|
-
if (voice >= 2 &&
|
|
1904
|
+
if (voice >= 2 && _chunkYPB3A3EYcjs.exceedsSpacing.call(void 0, upper, lower, maxSpacing)) {
|
|
1939
1905
|
found.push(
|
|
1940
1906
|
violation(
|
|
1941
1907
|
"spacing",
|
|
@@ -1975,8 +1941,8 @@ function pairViolations(transition) {
|
|
|
1975
1941
|
if (upperPrev === void 0 || upperCur === void 0 || lowerPrev === void 0 || lowerCur === void 0) {
|
|
1976
1942
|
continue;
|
|
1977
1943
|
}
|
|
1978
|
-
if (
|
|
1979
|
-
const fifth =
|
|
1944
|
+
if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
|
|
1945
|
+
const fifth = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, upperCur - lowerCur) === 7;
|
|
1980
1946
|
found.push(
|
|
1981
1947
|
violation(
|
|
1982
1948
|
fifth ? "parallelFifth" : "parallelOctave",
|
|
@@ -1987,7 +1953,7 @@ function pairViolations(transition) {
|
|
|
1987
1953
|
)
|
|
1988
1954
|
);
|
|
1989
1955
|
}
|
|
1990
|
-
if (upper === lower + 1 &&
|
|
1956
|
+
if (upper === lower + 1 && _chunkYPB3A3EYcjs.createsVoiceOverlap.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
|
|
1991
1957
|
found.push(
|
|
1992
1958
|
violation(
|
|
1993
1959
|
"overlap",
|
|
@@ -2004,110 +1970,38 @@ function pairViolations(transition) {
|
|
|
2004
1970
|
const bassCur = to.pitches[0];
|
|
2005
1971
|
const topPrev = from.pitches[voices - 1];
|
|
2006
1972
|
const topCur = to.pitches[voices - 1];
|
|
2007
|
-
if (voices >= 2 && bassPrev !== void 0 && bassCur !== void 0 && topPrev !== void 0 && topCur !== void 0 &&
|
|
1973
|
+
if (voices >= 2 && bassPrev !== void 0 && bassCur !== void 0 && topPrev !== void 0 && topCur !== void 0 && _chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, topPrev, topCur, bassPrev, bassCur)) {
|
|
2008
1974
|
found.push(
|
|
2009
1975
|
violation(
|
|
2010
1976
|
"hiddenPerfect",
|
|
2011
1977
|
[0, voices - 1],
|
|
2012
1978
|
from.index,
|
|
2013
1979
|
to.index,
|
|
2014
|
-
"The outer voices
|
|
1980
|
+
"The outer voices reach a perfect interval by similar motion, the upper one by leap"
|
|
2015
1981
|
)
|
|
2016
1982
|
);
|
|
2017
1983
|
}
|
|
2018
1984
|
return found;
|
|
2019
1985
|
}
|
|
2020
|
-
function inKey(pc, key) {
|
|
2021
|
-
return (key.modeMask12 >> _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - key.rootPc) & 1) === 1;
|
|
2022
|
-
}
|
|
2023
|
-
function isDominantSonority(chord) {
|
|
2024
|
-
const has = (semitones) => chord.intervals.some((interval) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, interval) === semitones);
|
|
2025
|
-
return chord.quality === "maj" || has(4) && has(10);
|
|
2026
|
-
}
|
|
2027
|
-
function isAppliedDominant(chord, next, key) {
|
|
2028
|
-
if (next === void 0 || !isDominantSonority(chord)) {
|
|
2029
|
-
return false;
|
|
2030
|
-
}
|
|
2031
|
-
const target = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + 5);
|
|
2032
|
-
return target !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc) && inKey(target, key) && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, next.rootPc) === target;
|
|
2033
|
-
}
|
|
2034
|
-
function isNeapolitan(chord, key) {
|
|
2035
|
-
return chord.quality === "maj" && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 1;
|
|
2036
|
-
}
|
|
2037
|
-
function soundsAugmentedSixth(moment, key) {
|
|
2038
|
-
const bass = moment.notes[0];
|
|
2039
|
-
if (bass === void 0 || _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, bass) !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc + 8)) {
|
|
2040
|
-
return false;
|
|
2041
|
-
}
|
|
2042
|
-
return moment.notes.some((note, voice) => {
|
|
2043
|
-
if (voice === 0) {
|
|
2044
|
-
return false;
|
|
2045
|
-
}
|
|
2046
|
-
const interval = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, bass, note);
|
|
2047
|
-
return (interval.number - 1) % 7 + 1 === 6 && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, interval.semitones) === 10;
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2050
|
-
function isChromaticHarmony(moment, next, key) {
|
|
2051
|
-
return isAppliedDominant(moment.chord, next, key) || isNeapolitan(moment.chord, key) || soundsAugmentedSixth(moment, key);
|
|
2052
|
-
}
|
|
2053
|
-
function approachedByStep(before, moment, voice) {
|
|
2054
|
-
const earlier = _optionalChain([before, 'optionalAccess', _24 => _24.notes, 'access', _25 => _25[voice]]);
|
|
2055
|
-
const later = moment.notes[voice];
|
|
2056
|
-
if (earlier === void 0 || later === void 0) {
|
|
2057
|
-
return false;
|
|
2058
|
-
}
|
|
2059
|
-
return _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, earlier, later).number === 2;
|
|
2060
|
-
}
|
|
2061
|
-
function isOuterVoice(voice, moment) {
|
|
2062
|
-
return voice === 0 || voice === moment.notes.length - 1;
|
|
2063
|
-
}
|
|
2064
1986
|
function crossRelationViolations(transition) {
|
|
2065
|
-
const found = [];
|
|
2066
1987
|
const { from, to, key, before, after } = transition;
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
if (earlier === void 0 || later === void 0) {
|
|
2077
|
-
continue;
|
|
2078
|
-
}
|
|
2079
|
-
const letter = _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, earlier.letter);
|
|
2080
|
-
if (letter !== _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, later.letter) || earlier.alter === later.alter) {
|
|
2081
|
-
continue;
|
|
2082
|
-
}
|
|
2083
|
-
if (chromatic || approachedByStep(before, from, first) || approachedByStep(from, to, second) || !isOuterVoice(first, from) && !isOuterVoice(second, to)) {
|
|
2084
|
-
continue;
|
|
2085
|
-
}
|
|
2086
|
-
const pair = `${letter}:${Math.min(first, second)}:${Math.max(first, second)}`;
|
|
2087
|
-
if (reported.has(pair)) {
|
|
2088
|
-
continue;
|
|
2089
|
-
}
|
|
2090
|
-
reported.add(pair);
|
|
2091
|
-
found.push(
|
|
2092
|
-
violation(
|
|
2093
|
-
"crossRelation",
|
|
2094
|
-
[first, second],
|
|
2095
|
-
from.index,
|
|
2096
|
-
to.index,
|
|
2097
|
-
`${_chunkQ2WGKJNNcjs.formatNote.call(void 0, bare(earlier))} is contradicted by ${_chunkQ2WGKJNNcjs.formatNote.call(void 0, bare(later))} in another voice`
|
|
2098
|
-
)
|
|
2099
|
-
);
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
return found;
|
|
1988
|
+
return crossRelations(from, to, key, { before: _optionalChain([before, 'optionalAccess', _22 => _22.notes]), after: _optionalChain([after, 'optionalAccess', _23 => _23.chord]) }).map(
|
|
1989
|
+
(clash) => violation(
|
|
1990
|
+
"crossRelation",
|
|
1991
|
+
[clash.earlierVoice, clash.laterVoice],
|
|
1992
|
+
from.index,
|
|
1993
|
+
to.index,
|
|
1994
|
+
`${_chunkXKSO2V5Icjs.formatNote.call(void 0, bare(clash.earlier))} is contradicted by ${_chunkXKSO2V5Icjs.formatNote.call(void 0, bare(clash.later))} in another voice`
|
|
1995
|
+
)
|
|
1996
|
+
);
|
|
2103
1997
|
}
|
|
2104
1998
|
function melodicViolations2(transition) {
|
|
2105
1999
|
const found = [];
|
|
2106
2000
|
const { from, to, key } = transition;
|
|
2107
|
-
const seventhPc = seventhPcOf(from.chord);
|
|
2108
|
-
const nextPcs = new Set(
|
|
2109
|
-
const leadingTonePc = leadingTonePcOf(key);
|
|
2110
|
-
const tonicPc =
|
|
2001
|
+
const seventhPc = _chunkYPB3A3EYcjs.seventhPcOf.call(void 0, from.chord);
|
|
2002
|
+
const nextPcs = new Set(_chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, to.chord));
|
|
2003
|
+
const leadingTonePc = _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key);
|
|
2004
|
+
const tonicPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc);
|
|
2111
2005
|
const voices = Math.min(from.notes.length, to.notes.length);
|
|
2112
2006
|
for (let voice = 0; voice < voices; voice += 1) {
|
|
2113
2007
|
const earlier = from.notes[voice];
|
|
@@ -2117,8 +2011,8 @@ function melodicViolations2(transition) {
|
|
|
2117
2011
|
if (earlier === void 0 || later === void 0 || earlierPitch === void 0 || laterPitch === void 0) {
|
|
2118
2012
|
continue;
|
|
2119
2013
|
}
|
|
2120
|
-
const interval =
|
|
2121
|
-
if (
|
|
2014
|
+
const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later);
|
|
2015
|
+
if (_chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, earlier, later)) {
|
|
2122
2016
|
found.push(
|
|
2123
2017
|
violation(
|
|
2124
2018
|
"augmentedMelodicInterval",
|
|
@@ -2129,8 +2023,8 @@ function melodicViolations2(transition) {
|
|
|
2129
2023
|
)
|
|
2130
2024
|
);
|
|
2131
2025
|
}
|
|
2132
|
-
const earlierPc =
|
|
2133
|
-
if (earlierPc === seventhPc && !nextPcs.has(earlierPc) && !isDescendingStep(interval)) {
|
|
2026
|
+
const earlierPc = _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, earlier);
|
|
2027
|
+
if (earlierPc === seventhPc && !nextPcs.has(earlierPc) && !_chunkYPB3A3EYcjs.isDescendingStep.call(void 0, interval)) {
|
|
2134
2028
|
found.push(
|
|
2135
2029
|
violation(
|
|
2136
2030
|
"unresolvedSeventh",
|
|
@@ -2141,7 +2035,8 @@ function melodicViolations2(transition) {
|
|
|
2141
2035
|
)
|
|
2142
2036
|
);
|
|
2143
2037
|
}
|
|
2144
|
-
|
|
2038
|
+
const inner = !isOuterVoice(voice, from.notes) && !isOuterVoice(voice, to.notes);
|
|
2039
|
+
if (earlierPc === leadingTonePc && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, from.chord, key) && nextPcs.has(tonicPc) && !nextPcs.has(earlierPc) && !_chunkYPB3A3EYcjs.isLeadingToneResolution.call(void 0, earlierPitch, laterPitch, key) && !(inner && _chunkYPB3A3EYcjs.isFrustratedLeadingTone.call(void 0, earlierPitch, laterPitch, to.chord))) {
|
|
2145
2040
|
found.push(
|
|
2146
2041
|
violation(
|
|
2147
2042
|
"unresolvedLeadingTone",
|
|
@@ -2156,24 +2051,30 @@ function melodicViolations2(transition) {
|
|
|
2156
2051
|
return found;
|
|
2157
2052
|
}
|
|
2158
2053
|
function spellVoicing(voicing, chord, key) {
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2054
|
+
_chunkXKSO2V5Icjs.assertArray.call(void 0, voicing, "voicing");
|
|
2055
|
+
_chunkYPB3A3EYcjs.assertChord.call(void 0, chord);
|
|
2056
|
+
const { tonic, scale } = _chunkYPB3A3EYcjs.resolveKey.call(void 0, key);
|
|
2057
|
+
return voicing.map((pitch) => _chunkYPB3A3EYcjs.spellPitch.call(void 0, pitch, tonic, scale, { chord }));
|
|
2058
|
+
}
|
|
2059
|
+
function checkPartWriting(voicings, chords, keyLike, opts) {
|
|
2060
|
+
_chunkXKSO2V5Icjs.assertArray.call(void 0, voicings, "voicings");
|
|
2061
|
+
_chunkXKSO2V5Icjs.assertArray.call(void 0, chords, "chords");
|
|
2062
|
+
const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
|
|
2063
|
+
const key = _chunkYPB3A3EYcjs.toKeyScale.call(void 0, keyLike);
|
|
2163
2064
|
if (voicings.length !== chords.length) {
|
|
2164
|
-
throw new (0,
|
|
2065
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
2165
2066
|
`checkPartWriting needs one chord per voicing; received ${voicings.length} voicings and ${chords.length} chords`
|
|
2166
2067
|
);
|
|
2167
2068
|
}
|
|
2168
|
-
const maxSpacing = resolveMaxSpacing(
|
|
2069
|
+
const maxSpacing = resolveMaxSpacing(asked);
|
|
2169
2070
|
const voiceCount = voicings.reduce((widest, voicing) => Math.max(widest, voicing.length), 0);
|
|
2170
2071
|
let ranges;
|
|
2171
|
-
if (
|
|
2072
|
+
if (asked.ranges === void 0) {
|
|
2172
2073
|
ranges = voiceCount === 4 ? SATB_RANGES : void 0;
|
|
2173
2074
|
} else {
|
|
2174
|
-
ranges = resolveRanges(
|
|
2075
|
+
ranges = resolveRanges(asked);
|
|
2175
2076
|
if (ranges.length < voiceCount) {
|
|
2176
|
-
throw new (0,
|
|
2077
|
+
throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
|
|
2177
2078
|
`checkPartWriting needs one range per voice; received ${ranges.length} ranges for ${voiceCount} voices`
|
|
2178
2079
|
);
|
|
2179
2080
|
}
|
|
@@ -2233,11 +2134,5 @@ function checkPartWriting(voicings, chords, key, opts) {
|
|
|
2233
2134
|
|
|
2234
2135
|
|
|
2235
2136
|
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
exports.TRANSPOSING_INSTRUMENTS = TRANSPOSING_INSTRUMENTS; exports.instrumentTransposition = instrumentTransposition; exports.toSoundingPitch = toSoundingPitch; exports.toWrittenPitch = toWrittenPitch; exports.scaleMatchesChord = scaleMatchesChord; exports.chordScales = chordScales; exports.avoidNotes = avoidNotes; exports.availableTensions = availableTensions; exports.chordScaleReport = chordScaleReport; exports.scalesForChanges = scalesForChanges; exports.figuredBassRealization = figuredBassRealization; exports.realizeFiguredBass = realizeFiguredBass; exports.figuredBassOf = figuredBassOf; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled; exports.checkSpecies = checkSpecies; exports.spellVoicing = spellVoicing; exports.checkPartWriting = checkPartWriting;
|
|
2243
|
-
//# sourceMappingURL=chunk-WWURE4XT.cjs.map
|
|
2137
|
+
exports.TRANSPOSING_INSTRUMENTS = TRANSPOSING_INSTRUMENTS; exports.instrumentTransposition = instrumentTransposition; exports.toSoundingPitch = toSoundingPitch; exports.toWrittenPitch = toWrittenPitch; exports.figuredBassRealization = figuredBassRealization; exports.realizeFiguredBass = realizeFiguredBass; exports.figuredBassOf = figuredBassOf; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled; exports.checkSpecies = checkSpecies; exports.spellVoicing = spellVoicing; exports.checkPartWriting = checkPartWriting;
|
|
2138
|
+
//# sourceMappingURL=chunk-RVS4G4MR.cjs.map
|