@libraz/libcantus 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -261
- package/README_ja.md +115 -0
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +213 -456
- package/dist/analyze/index.d.ts +213 -456
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/chunk-ELCTZUJJ.js +379 -0
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +103 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +96 -345
- package/dist/core/index.d.ts +96 -345
- package/dist/core/index.js +104 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +437 -1095
- package/dist/generate/index.d.ts +437 -1095
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +358 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +407 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4685 -243
- package/dist/model/index.d.ts +4685 -243
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +513 -173
- package/dist/theory/index.d.ts +513 -173
- package/dist/theory/index.js +118 -24
- package/package.json +16 -11
- package/dist/chunk-36N73KZ3.cjs +0 -246
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs +0 -3077
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs +0 -498
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js +0 -3077
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs +0 -679
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js +0 -479
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js +0 -1449
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs +0 -30
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs +0 -479
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js +0 -173
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-HKGYCQ34.js +0 -53
- package/dist/chunk-HKGYCQ34.js.map +0 -1
- package/dist/chunk-I24MV65U.js +0 -106
- package/dist/chunk-I24MV65U.js.map +0 -1
- package/dist/chunk-KMHOICIY.js +0 -679
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs +0 -1117
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NKIBQ6PT.js +0 -30
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs +0 -106
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs +0 -1449
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js +0 -327
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js +0 -845
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs +0 -327
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs +0 -845
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js +0 -1117
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-RUMU3UA6.cjs +0 -53
- package/dist/chunk-RUMU3UA6.cjs.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-XEBG3G53.js +0 -498
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js +0 -246
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs +0 -173
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- package/dist/function-BtiZb9i9.d.ts +0 -208
- package/dist/function-DNugAOrt.d.cts +0 -208
- package/dist/index-6VR8-kit.d.ts +0 -143
- package/dist/index-70p168VM.d.ts +0 -192
- package/dist/index-Be74mcon.d.cts +0 -152
- package/dist/index-BtXT6UQ0.d.cts +0 -192
- package/dist/index-C5UBbQ4G.d.ts +0 -152
- package/dist/index-C83mZI1P.d.ts +0 -242
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-OG65ocOr.d.cts +0 -272
- package/dist/index-OG65ocOr.d.ts +0 -272
- package/dist/index-tJhkmQgE.d.cts +0 -143
- package/dist/index-y_ak9_Hi.d.cts +0 -242
- package/dist/styled-DIRkk9Uz.d.ts +0 -506
- package/dist/styled-DJbkJQfj.d.cts +0 -506
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
package/dist/chunk-GQSGS2N4.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
borrowedSource,
|
|
3
|
-
chordToRoman,
|
|
4
|
-
detectChordBest,
|
|
5
|
-
functionOf,
|
|
6
|
-
isDiatonic,
|
|
7
|
-
parallelKey
|
|
8
|
-
} from "./chunk-KMHOICIY.js";
|
|
9
|
-
import {
|
|
10
|
-
chordPitchClasses,
|
|
11
|
-
diatonicTriad,
|
|
12
|
-
makeChord,
|
|
13
|
-
scaleTonesInDegreeOrder
|
|
14
|
-
} from "./chunk-D2FAJBYL.js";
|
|
15
|
-
import {
|
|
16
|
-
pitchClassOf
|
|
17
|
-
} from "./chunk-XEBG3G53.js";
|
|
18
|
-
|
|
19
|
-
// src/generate/reharmony/index.ts
|
|
20
|
-
function hasInterval(chord, semitone) {
|
|
21
|
-
return chord.intervals.some((iv) => pitchClassOf(iv) === semitone);
|
|
22
|
-
}
|
|
23
|
-
function isDominantType(chord) {
|
|
24
|
-
return hasInterval(chord, 4) && hasInterval(chord, 10);
|
|
25
|
-
}
|
|
26
|
-
function isStableTarget(chord) {
|
|
27
|
-
const hasFifth = hasInterval(chord, 7);
|
|
28
|
-
const hasThird = hasInterval(chord, 3) || hasInterval(chord, 4);
|
|
29
|
-
return hasFifth && hasThird && !isDominantType(chord);
|
|
30
|
-
}
|
|
31
|
-
function commonToneCount(a, b) {
|
|
32
|
-
const set = new Set(b);
|
|
33
|
-
return a.filter((pc) => set.has(pc)).length;
|
|
34
|
-
}
|
|
35
|
-
function triadQualityOf(chord) {
|
|
36
|
-
const semis = new Set(chord.intervals.map((interval) => pitchClassOf(interval)));
|
|
37
|
-
const hasThird = semis.has(4) ? "maj" : semis.has(3) ? "min" : void 0;
|
|
38
|
-
if (hasThird === void 0) {
|
|
39
|
-
return chord.quality;
|
|
40
|
-
}
|
|
41
|
-
if (semis.has(6) && !semis.has(7)) {
|
|
42
|
-
return hasThird === "min" ? "dim" : "majb5";
|
|
43
|
-
}
|
|
44
|
-
if (semis.has(8) && !semis.has(7)) {
|
|
45
|
-
return "aug";
|
|
46
|
-
}
|
|
47
|
-
return hasThird;
|
|
48
|
-
}
|
|
49
|
-
function diatonicTriadsOf(key) {
|
|
50
|
-
const degrees = scaleTonesInDegreeOrder(key).length;
|
|
51
|
-
const triads = [];
|
|
52
|
-
for (let degree = 0; degree < degrees; degree += 1) {
|
|
53
|
-
triads.push(diatonicTriad(degree, key));
|
|
54
|
-
}
|
|
55
|
-
return triads;
|
|
56
|
-
}
|
|
57
|
-
function sameChord(a, b) {
|
|
58
|
-
return pitchClassOf(a.rootPc) === pitchClassOf(b.rootPc) && a.quality === b.quality;
|
|
59
|
-
}
|
|
60
|
-
var THIRD_OFFSETS = [3, 4, 8, 9];
|
|
61
|
-
function substituteChord(chord, key, opts) {
|
|
62
|
-
const originalTriad = chordPitchClasses(makeChord(chord.rootPc, triadQualityOf(chord)));
|
|
63
|
-
const candidates = [];
|
|
64
|
-
if (isDominantType(chord)) {
|
|
65
|
-
candidates.push({ chord: makeChord(pitchClassOf(chord.rootPc + 6), "dom7"), type: "tritone" });
|
|
66
|
-
}
|
|
67
|
-
if (isStableTarget(chord)) {
|
|
68
|
-
candidates.push({
|
|
69
|
-
chord: makeChord(pitchClassOf(chord.rootPc + 7), "dom7"),
|
|
70
|
-
type: "secondaryDominant"
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
for (const triad of diatonicTriadsOf(key)) {
|
|
74
|
-
const offset = pitchClassOf(triad.rootPc - chord.rootPc);
|
|
75
|
-
if (THIRD_OFFSETS.includes(offset) && commonToneCount(originalTriad, chordPitchClasses(triad)) === 2) {
|
|
76
|
-
candidates.push({ chord: triad, type: "relative" });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const targetFunction = functionOf(chord, key);
|
|
80
|
-
for (const triad of diatonicTriadsOf(parallelKey(key))) {
|
|
81
|
-
if (functionOf(triad, key) === targetFunction && !isDiatonic(triad, key)) {
|
|
82
|
-
candidates.push({ chord: triad, type: "borrowed" });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
for (const offset of THIRD_OFFSETS) {
|
|
86
|
-
for (const quality of ["maj", "min"]) {
|
|
87
|
-
const mediant = makeChord(pitchClassOf(chord.rootPc + offset), quality);
|
|
88
|
-
if (commonToneCount(originalTriad, chordPitchClasses(mediant)) === 1 && !isDiatonic(mediant, key)) {
|
|
89
|
-
candidates.push({ chord: mediant, type: "chromaticMediant" });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const melodyPcs = opts?.melodyPcs?.map(pitchClassOf);
|
|
94
|
-
const seen = /* @__PURE__ */ new Set();
|
|
95
|
-
const results = [];
|
|
96
|
-
for (const candidate of candidates) {
|
|
97
|
-
if (sameChord(candidate.chord, chord)) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const dedupeKey = `${pitchClassOf(candidate.chord.rootPc)}:${candidate.chord.quality}`;
|
|
101
|
-
if (seen.has(dedupeKey)) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
seen.add(dedupeKey);
|
|
105
|
-
if (melodyPcs !== void 0) {
|
|
106
|
-
const pcs = new Set(chordPitchClasses(candidate.chord));
|
|
107
|
-
if (!melodyPcs.every((pc) => pcs.has(pc))) {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
results.push({
|
|
112
|
-
chord: candidate.chord,
|
|
113
|
-
type: candidate.type,
|
|
114
|
-
// A secondary dominant is named as the applied chord it is, so the
|
|
115
|
-
// numeral re-parses to the chord that was proposed rather than to a
|
|
116
|
-
// chromatic chord on the same root.
|
|
117
|
-
roman: chordToRoman(candidate.chord, key, {
|
|
118
|
-
applied: candidate.type === "secondaryDominant"
|
|
119
|
-
}),
|
|
120
|
-
function: functionOf(candidate.chord, key)
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return results;
|
|
124
|
-
}
|
|
125
|
-
function modalInterchangePalette(key) {
|
|
126
|
-
const palette = [];
|
|
127
|
-
const seen = /* @__PURE__ */ new Set();
|
|
128
|
-
const add = (chord) => {
|
|
129
|
-
const dedupeKey = `${pitchClassOf(chord.rootPc)}:${chord.quality}`;
|
|
130
|
-
if (seen.has(dedupeKey)) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
seen.add(dedupeKey);
|
|
134
|
-
palette.push({
|
|
135
|
-
chord,
|
|
136
|
-
roman: chordToRoman(chord, key),
|
|
137
|
-
source: borrowedSource(chord, key)
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
for (const triad of diatonicTriadsOf(parallelKey(key))) {
|
|
141
|
-
if (!isDiatonic(triad, key)) {
|
|
142
|
-
add(triad);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const neapolitan = makeChord(pitchClassOf(key.rootPc + 1), "maj");
|
|
146
|
-
if (!isDiatonic(neapolitan, key)) {
|
|
147
|
-
add(neapolitan);
|
|
148
|
-
}
|
|
149
|
-
return palette;
|
|
150
|
-
}
|
|
151
|
-
function negativeHarmonyMirror(chord, key) {
|
|
152
|
-
const tonic = pitchClassOf(key.rootPc);
|
|
153
|
-
const mirror = (p) => pitchClassOf(2 * tonic + 7 - p);
|
|
154
|
-
const pcs = chordPitchClasses(chord).map(mirror);
|
|
155
|
-
const bassPc = chord.bassPc !== void 0 ? mirror(chord.bassPc) : void 0;
|
|
156
|
-
const pitches = bassPc !== void 0 ? [bassPc - 12, ...pcs] : pcs;
|
|
157
|
-
const detected = detectChordBest(pitches);
|
|
158
|
-
if (detected) {
|
|
159
|
-
if (bassPc === void 0 && detected.bassPc !== void 0) {
|
|
160
|
-
return makeChord(detected.rootPc, detected.quality);
|
|
161
|
-
}
|
|
162
|
-
return detected;
|
|
163
|
-
}
|
|
164
|
-
const quality = chord.quality;
|
|
165
|
-
return makeChord(mirror(chord.rootPc), quality, bassPc);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export {
|
|
169
|
-
substituteChord,
|
|
170
|
-
modalInterchangePalette,
|
|
171
|
-
negativeHarmonyMirror
|
|
172
|
-
};
|
|
173
|
-
//# sourceMappingURL=chunk-GQSGS2N4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generate/reharmony/index.ts"],"sourcesContent":["/**\n * Reharmonization: chord substitution, modal-interchange palettes, and negative\n * harmony. These transforms take an existing chord (or a key) and propose\n * alternative harmonies that preserve a chosen relationship — a shared function,\n * a common tone, a tonicizing dominant, or a reflection across the key axis.\n *\n * Roots are pitch classes (0..11); no spelled key signature is required.\n */\n\nimport { detectChordBest } from '../../analyze/detect/index.js';\nimport {\n type BorrowedSource,\n borrowedSource,\n chordToRoman,\n functionOf,\n type HarmonicFunction,\n isDiatonic,\n parallelKey,\n} from '../../analyze/functional/index.js';\nimport { pitchClassOf as mod12 } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport {\n type Chord,\n type ChordQuality,\n chordPitchClasses,\n diatonicTriad,\n makeChord,\n} from '../../theory/chord/index.js';\nimport { scaleTonesInDegreeOrder } from '../../theory/scale/index.js';\n\n/**\n * The kind of substitution relationship a candidate realizes.\n *\n * @category Reharmonization\n */\nexport type SubstitutionType =\n | 'tritone'\n | 'relative'\n | 'borrowed'\n | 'chromaticMediant'\n | 'secondaryDominant';\n\n/**\n * A proposed chord substitution with its relationship, numeral, and function.\n *\n * @category Reharmonization\n */\nexport type Substitution = {\n chord: Chord;\n type: SubstitutionType;\n roman: string;\n function: HarmonicFunction;\n};\n\n/**\n * Options controlling which substitutions {@link substituteChord} returns.\n *\n * @category Reharmonization\n */\nexport type SubstituteOptions = {\n /**\n * Melody pitch classes that must remain chord tones. When given, only\n * substitutions whose pitch classes contain every listed pitch class are\n * kept, so the melody stays consonant against the new harmony.\n */\n melodyPcs?: number[];\n};\n\n/** Whether the chord's template carries a given interval (mod 12). */\nfunction hasInterval(chord: Chord, semitone: number): boolean {\n return chord.intervals.some((iv) => mod12(iv) === semitone);\n}\n\n/** Whether a chord is a dominant type: a major third with a minor seventh. */\nfunction isDominantType(chord: Chord): boolean {\n return hasInterval(chord, 4) && hasInterval(chord, 10);\n}\n\n/**\n * A chord that can be tonicized: it has a perfect fifth and a third but is not\n * itself a dominant seventh (which is an unstable, tonicizing sonority rather\n * than a stable target).\n */\nfunction isStableTarget(chord: Chord): boolean {\n const hasFifth = hasInterval(chord, 7);\n const hasThird = hasInterval(chord, 3) || hasInterval(chord, 4);\n return hasFifth && hasThird && !isDominantType(chord);\n}\n\n/** Count the pitch classes shared between two pitch-class sets. */\nfunction commonToneCount(a: number[], b: number[]): number {\n const set = new Set(b);\n return a.filter((pc) => set.has(pc)).length;\n}\n\n/** The plain triad quality underlying a chord's own quality. */\nfunction triadQualityOf(chord: Chord): ChordQuality {\n const semis = new Set(chord.intervals.map((interval) => mod12(interval)));\n const hasThird = semis.has(4) ? 'maj' : semis.has(3) ? 'min' : undefined;\n if (hasThird === undefined) {\n return chord.quality; // sus, power, and other non-tertian chords stand alone\n }\n if (semis.has(6) && !semis.has(7)) {\n return hasThird === 'min' ? 'dim' : 'majb5';\n }\n if (semis.has(8) && !semis.has(7)) {\n return 'aug';\n }\n return hasThird;\n}\n\n/** The diatonic triads of a key, one per scale degree. */\nfunction diatonicTriadsOf(key: KeyScale): Chord[] {\n const degrees = scaleTonesInDegreeOrder(key).length;\n const triads: Chord[] = [];\n for (let degree = 0; degree < degrees; degree += 1) {\n triads.push(diatonicTriad(degree, key));\n }\n return triads;\n}\n\n/** Whether two chords share the same root pitch class and quality. */\nfunction sameChord(a: Chord, b: Chord): boolean {\n return mod12(a.rootPc) === mod12(b.rootPc) && a.quality === b.quality;\n}\n\n/** Root offsets (semitones) that form a third above or below a root. */\nconst THIRD_OFFSETS = [3, 4, 8, 9] as const;\n\n/**\n * Propose substitutions for a chord within a key.\n *\n * Candidates are generated by several relationships and deduplicated against the\n * original chord and each other:\n *\n * - `tritone`: for a dominant-type chord, the dominant seventh a tritone away.\n * - `secondaryDominant`: for a stable target, the dominant seventh a fifth\n * above its root (tonicizing it).\n * - `relative`: a diatonic triad a third away that shares two common tones.\n * - `borrowed`: a triad from the parallel mode with the same harmonic function.\n * - `chromaticMediant`: a major/minor triad a third away sharing one common\n * tone.\n *\n * Each result carries its Roman numeral and harmonic function in `key`. When\n * `opts.melodyPcs` is given, only substitutions whose pitch classes contain all\n * of those pitch classes are returned.\n *\n * @param chord The chord to reharmonize.\n * @param key The prevailing key.\n * @param opts Optional melody-preservation constraint.\n * @returns The deduplicated substitution candidates.\n * @example\n * ```ts\n * import { substituteChord, makeChord, majorKey } from '@libraz/libcantus';\n * const subs = substituteChord(makeChord(7, 'dom7'), majorKey(0));\n * // includes the tritone sub Db7 (rootPc 1, quality 'dom7')\n * ```\n * @category Reharmonization\n */\nexport function substituteChord(\n chord: Chord,\n key: KeyScale,\n opts?: SubstituteOptions,\n): Substitution[] {\n // The relative test counts common tones against the chord's triad, not its\n // full pitch-class set: an exact count of two would otherwise be decided by\n // how many tensions the input carries, so C proposes both Em and Am while\n // Cmaj7 proposes only Am — penalising the candidate that overlaps *more*.\n const originalTriad = chordPitchClasses(makeChord(chord.rootPc, triadQualityOf(chord)));\n const candidates: { chord: Chord; type: SubstitutionType }[] = [];\n\n // Tritone substitution: only for dominant-type chords.\n if (isDominantType(chord)) {\n candidates.push({ chord: makeChord(mod12(chord.rootPc + 6), 'dom7'), type: 'tritone' });\n }\n\n // Secondary dominant: the V7 tonicizing a stable target.\n if (isStableTarget(chord)) {\n candidates.push({\n chord: makeChord(mod12(chord.rootPc + 7), 'dom7'),\n type: 'secondaryDominant',\n });\n }\n\n // Relative: a diatonic triad a third away sharing two common tones.\n for (const triad of diatonicTriadsOf(key)) {\n const offset = mod12(triad.rootPc - chord.rootPc);\n if (\n (THIRD_OFFSETS as readonly number[]).includes(offset) &&\n commonToneCount(originalTriad, chordPitchClasses(triad)) === 2\n ) {\n candidates.push({ chord: triad, type: 'relative' });\n }\n }\n\n // Borrowed: a parallel-mode triad sharing the original's harmonic function.\n const targetFunction = functionOf(chord, key);\n for (const triad of diatonicTriadsOf(parallelKey(key))) {\n if (functionOf(triad, key) === targetFunction && !isDiatonic(triad, key)) {\n candidates.push({ chord: triad, type: 'borrowed' });\n }\n }\n\n // Chromatic mediants: major/minor triads a third away sharing one common tone.\n for (const offset of THIRD_OFFSETS) {\n for (const quality of ['maj', 'min'] as const) {\n const mediant = makeChord(mod12(chord.rootPc + offset), quality);\n if (\n commonToneCount(originalTriad, chordPitchClasses(mediant)) === 1 &&\n !isDiatonic(mediant, key)\n ) {\n candidates.push({ chord: mediant, type: 'chromaticMediant' });\n }\n }\n }\n\n const melodyPcs = opts?.melodyPcs?.map(mod12);\n const seen = new Set<string>();\n const results: Substitution[] = [];\n for (const candidate of candidates) {\n if (sameChord(candidate.chord, chord)) {\n continue;\n }\n const dedupeKey = `${mod12(candidate.chord.rootPc)}:${candidate.chord.quality}`;\n if (seen.has(dedupeKey)) {\n continue;\n }\n seen.add(dedupeKey);\n if (melodyPcs !== undefined) {\n const pcs = new Set(chordPitchClasses(candidate.chord));\n if (!melodyPcs.every((pc) => pcs.has(pc))) {\n continue;\n }\n }\n results.push({\n chord: candidate.chord,\n type: candidate.type,\n // A secondary dominant is named as the applied chord it is, so the\n // numeral re-parses to the chord that was proposed rather than to a\n // chromatic chord on the same root.\n roman: chordToRoman(candidate.chord, key, {\n applied: candidate.type === 'secondaryDominant',\n }),\n function: functionOf(candidate.chord, key),\n });\n }\n return results;\n}\n\n/**\n * A borrowed chord in the modal-interchange palette of a key.\n *\n * @category Reharmonization\n */\nexport type BorrowedChord = {\n chord: Chord;\n roman: string;\n source: BorrowedSource;\n};\n\n/**\n * The modal-interchange palette of a key.\n *\n * Lists the diatonic triads of the {@link parallelKey} that are not diatonic to\n * `key` (the classic borrowed chords such as `iv`, `bVI`, and `bVII` in a major\n * key), plus the Neapolitan (a major triad on the flat second degree). Each\n * carries its Roman numeral and {@link borrowedSource} relative to `key`.\n *\n * @param key The prevailing key.\n * @returns The borrowed-chord palette.\n * @category Reharmonization\n */\nexport function modalInterchangePalette(key: KeyScale): BorrowedChord[] {\n const palette: BorrowedChord[] = [];\n const seen = new Set<string>();\n const add = (chord: Chord) => {\n const dedupeKey = `${mod12(chord.rootPc)}:${chord.quality}`;\n if (seen.has(dedupeKey)) {\n return;\n }\n seen.add(dedupeKey);\n palette.push({\n chord,\n roman: chordToRoman(chord, key),\n source: borrowedSource(chord, key),\n });\n };\n\n for (const triad of diatonicTriadsOf(parallelKey(key))) {\n if (!isDiatonic(triad, key)) {\n add(triad);\n }\n }\n // The Neapolitan (major triad on b2) sits outside both parallel modes, so it\n // is added explicitly — unless b2 is already diatonic (e.g. in a Phrygian key,\n // where it is a native chord rather than a borrowing).\n const neapolitan = makeChord(mod12(key.rootPc + 1), 'maj');\n if (!isDiatonic(neapolitan, key)) {\n add(neapolitan);\n }\n return palette;\n}\n\n/**\n * Reflect a chord across the tonic–dominant axis of a key (negative harmony).\n *\n * Every pitch class `p` maps to `(2*T + 7 - p) mod 12`, where `T` is the key\n * tonic — the reflection that swaps the tonic with the dominant. The mirrored\n * pitch classes (including the bass, if set) are reassembled into a chord by\n * {@link detectChordBest}; when no chord is recognized, the mirrored root and\n * the original quality are used as a fallback.\n *\n * @param chord The chord to reflect.\n * @param key The prevailing key.\n * @returns The negative-harmony counterpart of the chord.\n * @example\n * ```ts\n * import { negativeHarmonyMirror, makeChord, majorKey } from '@libraz/libcantus';\n * negativeHarmonyMirror(makeChord(7, 'maj'), majorKey(0));\n * // G major reflected across C's axis => F minor (rootPc 5, quality 'min')\n * ```\n * @category Reharmonization\n */\nexport function negativeHarmonyMirror(chord: Chord, key: KeyScale): Chord {\n const tonic = mod12(key.rootPc);\n const mirror = (p: number) => mod12(2 * tonic + 7 - p);\n const pcs = chordPitchClasses(chord).map(mirror);\n const bassPc = chord.bassPc !== undefined ? mirror(chord.bassPc) : undefined;\n // Place the mirrored bass an octave below so it is recognized as the lowest\n // note and preserved through detection.\n const pitches = bassPc !== undefined ? [bassPc - 12, ...pcs] : pcs;\n const detected = detectChordBest(pitches);\n if (detected) {\n // Without an input bass the mirrored tones are octave-less, so detection may\n // read the lowest pitch class as a slash bass and report a spurious\n // inversion. Restore the recognized chord to root position in that case.\n if (bassPc === undefined && detected.bassPc !== undefined) {\n return makeChord(detected.rootPc, detected.quality);\n }\n return detected;\n }\n const quality: ChordQuality = chord.quality;\n return makeChord(mirror(chord.rootPc), quality, bassPc);\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-HKGYCQ34.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InvalidInputError,
|
|
3
|
-
assertFiniteNumber,
|
|
4
|
-
assertInteger,
|
|
5
|
-
assertRange
|
|
6
|
-
} from "./chunk-XEBG3G53.js";
|
|
7
|
-
|
|
8
|
-
// src/core/random/index.ts
|
|
9
|
-
function createRng(seed) {
|
|
10
|
-
assertInteger(seed, "seed", 0, 4294967295);
|
|
11
|
-
let state = seed >>> 0;
|
|
12
|
-
const next = () => {
|
|
13
|
-
state = state + 1831565813 >>> 0;
|
|
14
|
-
let t = state;
|
|
15
|
-
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
16
|
-
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
17
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
next,
|
|
21
|
-
prob: (p) => {
|
|
22
|
-
assertRange(p, 0, 1, "probability");
|
|
23
|
-
return next() < p;
|
|
24
|
-
},
|
|
25
|
-
range: (lo, hi) => {
|
|
26
|
-
assertInteger(lo, "range lower bound");
|
|
27
|
-
assertInteger(hi, "range upper bound");
|
|
28
|
-
if (lo > hi) {
|
|
29
|
-
throw new InvalidInputError(
|
|
30
|
-
`range lower bound must not exceed upper bound; received ${lo} > ${hi}`
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
assertInteger(hi - lo, "range span", 0);
|
|
34
|
-
return lo + Math.floor(next() * (hi - lo + 1));
|
|
35
|
-
},
|
|
36
|
-
float: (lo, hi) => {
|
|
37
|
-
assertFiniteNumber(lo, "float lower bound");
|
|
38
|
-
assertFiniteNumber(hi, "float upper bound");
|
|
39
|
-
if (lo > hi) {
|
|
40
|
-
throw new InvalidInputError(
|
|
41
|
-
`float lower bound must not exceed upper bound; received ${lo} > ${hi}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
assertFiniteNumber(hi - lo, "float span");
|
|
45
|
-
return lo + next() * (hi - lo);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export {
|
|
51
|
-
createRng
|
|
52
|
-
};
|
|
53
|
-
//# sourceMappingURL=chunk-HKGYCQ34.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/random/index.ts"],"sourcesContent":["/**\n * Deterministic seeded pseudo-random number generation shared across the\n * generative modules (rhythm, drums, bass, groove, counter-melody). A single\n * mulberry32 core exposes the small set of samplers those modules need; given a\n * seed the stream is fully reproducible.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport { assertFiniteNumber, assertInteger, assertRange } from '../validation/index.js';\n\n/**\n * A deterministic PRNG with the sampling helpers the generators need.\n *\n * @category Utilities\n */\nexport type Rng = {\n /** Next float in [0, 1). */\n next: () => number;\n /** True with probability `p`. */\n prob: (p: number) => boolean;\n /** Integer in the inclusive range [lo, hi]. */\n range: (lo: number, hi: number) => number;\n /** Float in [lo, hi). */\n float: (lo: number, hi: number) => number;\n};\n\n/**\n * Create a seeded PRNG (mulberry32) exposing the shared samplers.\n *\n * @param seed The 32-bit seed; the same seed always yields the same stream.\n * @returns The seeded generator.\n * @example\n * ```ts\n * import { createRng } from '@libraz/libcantus';\n * const rng = createRng(42);\n * rng.next(); // deterministic float in [0, 1)\n * rng.range(1, 6); // deterministic integer in [1, 6]\n * ```\n * @category Utilities\n */\nexport function createRng(seed: number): Rng {\n // The state is 32 bits, so a wider or fractional seed would be truncated —\n // silently mapping distinct seeds onto one sequence.\n assertInteger(seed, 'seed', 0, 0xffffffff);\n let state = seed >>> 0;\n const next = () => {\n state = (state + 0x6d2b79f5) >>> 0;\n let t = state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n return {\n next,\n prob: (p) => {\n // Validate first: a draw consumed by a rejected call shifts the stream,\n // so a caller who catches the error gets a different sequence than one\n // who never made the call.\n assertRange(p, 0, 1, 'probability');\n return next() < p;\n },\n range: (lo, hi) => {\n assertInteger(lo, 'range lower bound');\n assertInteger(hi, 'range upper bound');\n if (lo > hi) {\n throw new InvalidInputError(\n `range lower bound must not exceed upper bound; received ${lo} > ${hi}`,\n );\n }\n assertInteger(hi - lo, 'range span', 0);\n return lo + Math.floor(next() * (hi - lo + 1));\n },\n float: (lo, hi) => {\n assertFiniteNumber(lo, 'float lower bound');\n assertFiniteNumber(hi, 'float upper bound');\n if (lo > hi) {\n throw new InvalidInputError(\n `float lower bound must not exceed upper bound; received ${lo} > ${hi}`,\n );\n }\n assertFiniteNumber(hi - lo, 'float span');\n return lo + next() * (hi - lo);\n },\n };\n}\n"],"mappings":";;;;;;;;AAwCO,SAAS,UAAU,MAAmB;AAG3C,gBAAc,MAAM,QAAQ,GAAG,UAAU;AACzC,MAAI,QAAQ,SAAS;AACrB,QAAM,OAAO,MAAM;AACjB,YAAS,QAAQ,eAAgB;AACjC,QAAI,IAAI;AACR,QAAI,KAAK,KAAK,IAAK,MAAM,IAAK,IAAI,CAAC;AACnC,SAAK,IAAI,KAAK,KAAK,IAAK,MAAM,GAAI,IAAI,EAAE;AACxC,aAAS,IAAK,MAAM,QAAS,KAAK;AAAA,EACpC;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,CAAC,MAAM;AAIX,kBAAY,GAAG,GAAG,GAAG,aAAa;AAClC,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,IACA,OAAO,CAAC,IAAI,OAAO;AACjB,oBAAc,IAAI,mBAAmB;AACrC,oBAAc,IAAI,mBAAmB;AACrC,UAAI,KAAK,IAAI;AACX,cAAM,IAAI;AAAA,UACR,2DAA2D,EAAE,MAAM,EAAE;AAAA,QACvE;AAAA,MACF;AACA,oBAAc,KAAK,IAAI,cAAc,CAAC;AACtC,aAAO,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,EAAE;AAAA,IAC/C;AAAA,IACA,OAAO,CAAC,IAAI,OAAO;AACjB,yBAAmB,IAAI,mBAAmB;AAC1C,yBAAmB,IAAI,mBAAmB;AAC1C,UAAI,KAAK,IAAI;AACX,cAAM,IAAI;AAAA,UACR,2DAA2D,EAAE,MAAM,EAAE;AAAA,QACvE;AAAA,MACF;AACA,yBAAmB,KAAK,IAAI,YAAY;AACxC,aAAO,KAAK,KAAK,KAAK,KAAK;AAAA,IAC7B;AAAA,EACF;AACF;","names":[]}
|
package/dist/chunk-I24MV65U.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InvalidInputError,
|
|
3
|
-
assertFiniteNumber,
|
|
4
|
-
assertInteger,
|
|
5
|
-
assertPositiveInt,
|
|
6
|
-
assertRange
|
|
7
|
-
} from "./chunk-XEBG3G53.js";
|
|
8
|
-
|
|
9
|
-
// src/core/tuning/index.ts
|
|
10
|
-
var TWELVE_TET = Object.freeze({ refStep: 69, refFreq: 440, divisions: 12 });
|
|
11
|
-
function assertTuning(tuning) {
|
|
12
|
-
assertFiniteNumber(tuning.refStep, "tuning.refStep");
|
|
13
|
-
assertRange(tuning.refFreq, Number.MIN_VALUE, Number.MAX_VALUE, "tuning.refFreq");
|
|
14
|
-
assertPositiveInt(tuning.divisions, "tuning.divisions");
|
|
15
|
-
return tuning;
|
|
16
|
-
}
|
|
17
|
-
function edo(n, refFreq = 440, refStep = 69) {
|
|
18
|
-
assertPositiveInt(n, "EDO divisions");
|
|
19
|
-
assertRange(refFreq, Number.MIN_VALUE, Number.MAX_VALUE, "reference frequency");
|
|
20
|
-
assertFiniteNumber(refStep, "reference step");
|
|
21
|
-
return { refStep, refFreq, divisions: n };
|
|
22
|
-
}
|
|
23
|
-
function frequencyOf(step, tuning = TWELVE_TET) {
|
|
24
|
-
assertFiniteNumber(step, "step");
|
|
25
|
-
assertTuning(tuning);
|
|
26
|
-
const result = tuning.refFreq * 2 ** ((step - tuning.refStep) / tuning.divisions);
|
|
27
|
-
return assertRange(result, Number.MIN_VALUE, Number.MAX_VALUE, "frequency result");
|
|
28
|
-
}
|
|
29
|
-
function nearestStep(freq, tuning = TWELVE_TET) {
|
|
30
|
-
assertRange(freq, Number.MIN_VALUE, Number.MAX_VALUE, "frequency");
|
|
31
|
-
assertTuning(tuning);
|
|
32
|
-
return Math.round(tuning.refStep + tuning.divisions * Math.log2(freq / tuning.refFreq));
|
|
33
|
-
}
|
|
34
|
-
function stepOf(freq, tuning = TWELVE_TET) {
|
|
35
|
-
assertRange(freq, Number.MIN_VALUE, Number.MAX_VALUE, "frequency");
|
|
36
|
-
assertTuning(tuning);
|
|
37
|
-
return tuning.refStep + tuning.divisions * Math.log2(freq / tuning.refFreq);
|
|
38
|
-
}
|
|
39
|
-
function centsFromNearestStep(freq, tuning = TWELVE_TET) {
|
|
40
|
-
const exact = stepOf(freq, tuning);
|
|
41
|
-
return centsOfSteps(exact - Math.round(exact), tuning);
|
|
42
|
-
}
|
|
43
|
-
function stepsOfCents(cents, tuning = TWELVE_TET) {
|
|
44
|
-
assertFiniteNumber(cents, "cents");
|
|
45
|
-
assertTuning(tuning);
|
|
46
|
-
return assertFiniteNumber(cents * tuning.divisions / 1200, "steps result");
|
|
47
|
-
}
|
|
48
|
-
function centsToRatio(cents) {
|
|
49
|
-
assertFiniteNumber(cents, "cents");
|
|
50
|
-
return 2 ** (cents / 1200);
|
|
51
|
-
}
|
|
52
|
-
function centsBetweenFreq(a, b) {
|
|
53
|
-
assertRange(a, Number.MIN_VALUE, Number.MAX_VALUE, "first frequency");
|
|
54
|
-
assertRange(b, Number.MIN_VALUE, Number.MAX_VALUE, "second frequency");
|
|
55
|
-
return 1200 * (Math.log2(b) - Math.log2(a));
|
|
56
|
-
}
|
|
57
|
-
function centsOfSteps(steps, tuning = TWELVE_TET) {
|
|
58
|
-
assertFiniteNumber(steps, "steps");
|
|
59
|
-
assertTuning(tuning);
|
|
60
|
-
return assertFiniteNumber(steps * 1200 / tuning.divisions, "cents result");
|
|
61
|
-
}
|
|
62
|
-
function ratioToCents(numerator, denominator) {
|
|
63
|
-
assertRange(numerator, Number.MIN_VALUE, Number.MAX_VALUE, "ratio numerator");
|
|
64
|
-
assertRange(denominator, Number.MIN_VALUE, Number.MAX_VALUE, "ratio denominator");
|
|
65
|
-
return 1200 * (Math.log2(numerator) - Math.log2(denominator));
|
|
66
|
-
}
|
|
67
|
-
var JUST_RATIOS = Object.freeze({
|
|
68
|
-
0: [1, 1],
|
|
69
|
-
1: [16, 15],
|
|
70
|
-
2: [9, 8],
|
|
71
|
-
3: [6, 5],
|
|
72
|
-
4: [5, 4],
|
|
73
|
-
5: [4, 3],
|
|
74
|
-
6: [45, 32],
|
|
75
|
-
7: [3, 2],
|
|
76
|
-
8: [8, 5],
|
|
77
|
-
9: [5, 3],
|
|
78
|
-
10: [9, 5],
|
|
79
|
-
11: [15, 8],
|
|
80
|
-
12: [2, 1]
|
|
81
|
-
});
|
|
82
|
-
function justDeviationCents(semitoneClass) {
|
|
83
|
-
assertInteger(semitoneClass, "semitone class", 0, 12);
|
|
84
|
-
const ratio = JUST_RATIOS[semitoneClass];
|
|
85
|
-
if (!ratio) {
|
|
86
|
-
throw new InvalidInputError(`semitone class has no just ratio: ${semitoneClass}`);
|
|
87
|
-
}
|
|
88
|
-
return ratioToCents(ratio[0], ratio[1]) - semitoneClass * 100;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export {
|
|
92
|
-
TWELVE_TET,
|
|
93
|
-
edo,
|
|
94
|
-
frequencyOf,
|
|
95
|
-
nearestStep,
|
|
96
|
-
stepOf,
|
|
97
|
-
centsFromNearestStep,
|
|
98
|
-
stepsOfCents,
|
|
99
|
-
centsToRatio,
|
|
100
|
-
centsBetweenFreq,
|
|
101
|
-
centsOfSteps,
|
|
102
|
-
ratioToCents,
|
|
103
|
-
JUST_RATIOS,
|
|
104
|
-
justDeviationCents
|
|
105
|
-
};
|
|
106
|
-
//# sourceMappingURL=chunk-I24MV65U.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/tuning/index.ts"],"sourcesContent":["/**\n * Tuning and microtonality: frequency conversion, cents, arbitrary equal\n * temperaments (EDO), and just-intonation ratios.\n *\n * The rest of the library reasons in twelve-tone pitch classes, which is a\n * deliberate scope choice for tonal theory. This module is the escape hatch for\n * anyone who needs actual frequencies, cents, non-12 equal temperaments, or the\n * acoustic (just) tuning behind the tempered intervals.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport {\n assertFiniteNumber,\n assertInteger,\n assertPositiveInt,\n assertRange,\n} from '../validation/index.js';\n\n/**\n * An equal-tempered tuning: a reference pitch and a number of equal divisions of\n * the octave. With `divisions === 12` a step index is an ordinary MIDI number.\n *\n * @category Pitch & Intervals\n */\nexport type Tuning = {\n /** Step index (MIDI number when `divisions` is 12) whose frequency is `refFreq`. */\n readonly refStep: number;\n /** Frequency in Hz of `refStep`. */\n readonly refFreq: number;\n /** Equal divisions of the octave (12 standard; e.g. 19 or 31 for microtonal). */\n readonly divisions: number;\n};\n\n/**\n * Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.\n *\n * @category Pitch & Intervals\n */\nexport const TWELVE_TET: Tuning = Object.freeze({ refStep: 69, refFreq: 440, divisions: 12 });\n\nfunction assertTuning(tuning: Tuning): Tuning {\n assertFiniteNumber(tuning.refStep, 'tuning.refStep');\n assertRange(tuning.refFreq, Number.MIN_VALUE, Number.MAX_VALUE, 'tuning.refFreq');\n assertPositiveInt(tuning.divisions, 'tuning.divisions');\n return tuning;\n}\n\n/**\n * Build an equal temperament with `n` divisions of the octave.\n *\n * @param n Divisions of the octave (e.g. 19, 24, 31).\n * @param refFreq Reference frequency in Hz (default 440).\n * @param refStep Step index of the reference (default 69).\n * @returns The tuning.\n * @example\n * ```ts\n * import { edo, frequencyOf } from '@libraz/libcantus';\n * const et19 = edo(19); // { refStep: 69, refFreq: 440, divisions: 19 }\n * frequencyOf(70, et19); // one 19-EDO step above A4\n * ```\n * @category Pitch & Intervals\n */\nexport function edo(n: number, refFreq = 440, refStep = 69): Tuning {\n assertPositiveInt(n, 'EDO divisions');\n assertRange(refFreq, Number.MIN_VALUE, Number.MAX_VALUE, 'reference frequency');\n assertFiniteNumber(refStep, 'reference step');\n return { refStep, refFreq, divisions: n };\n}\n\n/**\n * Frequency in Hz of a step index under a tuning.\n *\n * @param step Step index (a MIDI number under 12-EDO).\n * @param tuning The tuning (default 12-TET).\n * @returns The frequency in Hz.\n * @example\n * ```ts\n * import { frequencyOf } from '@libraz/libcantus';\n * frequencyOf(69); // 440 (A4 in 12-TET)\n * frequencyOf(60); // middle C in Hz\n * ```\n * @category Pitch & Intervals\n */\nexport function frequencyOf(step: number, tuning: Tuning = TWELVE_TET): number {\n assertFiniteNumber(step, 'step');\n assertTuning(tuning);\n const result = tuning.refFreq * 2 ** ((step - tuning.refStep) / tuning.divisions);\n return assertRange(result, Number.MIN_VALUE, Number.MAX_VALUE, 'frequency result');\n}\n\n/**\n * Nearest step index to a frequency under a tuning (the inverse of\n * {@link frequencyOf}, rounded).\n *\n * @param freq Frequency in Hz.\n * @param tuning The tuning (default 12-TET).\n * @returns The nearest step index.\n * @category Pitch & Intervals\n */\nexport function nearestStep(freq: number, tuning: Tuning = TWELVE_TET): number {\n assertRange(freq, Number.MIN_VALUE, Number.MAX_VALUE, 'frequency');\n assertTuning(tuning);\n return Math.round(tuning.refStep + tuning.divisions * Math.log2(freq / tuning.refFreq));\n}\n\n/**\n * Step index of a frequency under a tuning, unrounded.\n *\n * The exact inverse of {@link frequencyOf}. {@link nearestStep} rounds it,\n * which discards how far off the pitch actually is — the figure a tuner\n * display, a pitch-bend amount, or an intonation check is built on.\n *\n * @param freq Frequency in Hz.\n * @param tuning The tuning (default 12-TET).\n * @returns The fractional step index.\n * @example\n * ```ts\n * import { stepOf } from '@libraz/libcantus';\n * stepOf(442); // slightly above 69, the step for A440\n * ```\n * @category Pitch & Intervals\n */\nexport function stepOf(freq: number, tuning: Tuning = TWELVE_TET): number {\n assertRange(freq, Number.MIN_VALUE, Number.MAX_VALUE, 'frequency');\n assertTuning(tuning);\n return tuning.refStep + tuning.divisions * Math.log2(freq / tuning.refFreq);\n}\n\n/**\n * How far a frequency sits from its nearest step, in cents.\n *\n * Positive means sharp of the step, negative flat.\n *\n * @param freq Frequency in Hz.\n * @param tuning The tuning (default 12-TET).\n * @returns The signed deviation in cents.\n * @example\n * ```ts\n * import { centsFromNearestStep } from '@libraz/libcantus';\n * Math.round(centsFromNearestStep(442)); // 8\n * ```\n * @category Pitch & Intervals\n */\nexport function centsFromNearestStep(freq: number, tuning: Tuning = TWELVE_TET): number {\n const exact = stepOf(freq, tuning);\n return centsOfSteps(exact - Math.round(exact), tuning);\n}\n\n/**\n * Steps spanned by an interval in cents, unrounded — the inverse of\n * {@link centsOfSteps}.\n *\n * @param cents The interval in cents.\n * @param tuning The tuning (default 12-TET).\n * @returns The fractional step count.\n * @category Pitch & Intervals\n */\nexport function stepsOfCents(cents: number, tuning: Tuning = TWELVE_TET): number {\n assertFiniteNumber(cents, 'cents');\n assertTuning(tuning);\n return assertFiniteNumber((cents * tuning.divisions) / 1200, 'steps result');\n}\n\n/**\n * The frequency ratio an interval in cents spans — the inverse of\n * {@link ratioToCents}.\n *\n * Multiply a frequency by this to move it by that many cents, which is how a\n * cents offset (a pitch-bend spec, a tuning table entry) is applied.\n *\n * @param cents The interval in cents.\n * @returns The frequency ratio.\n * @example\n * ```ts\n * import { centsToRatio } from '@libraz/libcantus';\n * 440 * centsToRatio(14); // A440 raised by 14 cents\n * ```\n * @category Pitch & Intervals\n */\nexport function centsToRatio(cents: number): number {\n assertFiniteNumber(cents, 'cents');\n return 2 ** (cents / 1200);\n}\n\n/**\n * Interval in cents between two frequencies.\n *\n * @param a Lower/first frequency in Hz.\n * @param b Upper/second frequency in Hz.\n * @returns Cents from `a` to `b` (negative if `b` is lower).\n * @category Pitch & Intervals\n */\nexport function centsBetweenFreq(a: number, b: number): number {\n assertRange(a, Number.MIN_VALUE, Number.MAX_VALUE, 'first frequency');\n assertRange(b, Number.MIN_VALUE, Number.MAX_VALUE, 'second frequency');\n return 1200 * (Math.log2(b) - Math.log2(a));\n}\n\n/**\n * Cents spanned by a number of equal-temperament steps.\n *\n * @param steps Number of steps.\n * @param tuning The tuning (default 12-TET).\n * @returns The cents.\n * @category Pitch & Intervals\n */\nexport function centsOfSteps(steps: number, tuning: Tuning = TWELVE_TET): number {\n assertFiniteNumber(steps, 'steps');\n assertTuning(tuning);\n return assertFiniteNumber((steps * 1200) / tuning.divisions, 'cents result');\n}\n\n/**\n * Cents of a frequency ratio, e.g. `ratioToCents(3, 2)` ≈ 701.955 for the just\n * perfect fifth.\n *\n * @param numerator Ratio numerator.\n * @param denominator Ratio denominator.\n * @returns The interval in cents.\n * @category Pitch & Intervals\n */\nexport function ratioToCents(numerator: number, denominator: number): number {\n assertRange(numerator, Number.MIN_VALUE, Number.MAX_VALUE, 'ratio numerator');\n assertRange(denominator, Number.MIN_VALUE, Number.MAX_VALUE, 'ratio denominator');\n return 1200 * (Math.log2(numerator) - Math.log2(denominator));\n}\n\n/**\n * Five-limit just-intonation ratios for the twelve interval classes above a\n * unison, indexed by semitone class (0..12).\n *\n * @category Pitch & Intervals\n */\nexport const JUST_RATIOS: Readonly<Record<number, readonly [number, number]>> = Object.freeze({\n 0: [1, 1],\n 1: [16, 15],\n 2: [9, 8],\n 3: [6, 5],\n 4: [5, 4],\n 5: [4, 3],\n 6: [45, 32],\n 7: [3, 2],\n 8: [8, 5],\n 9: [5, 3],\n 10: [9, 5],\n 11: [15, 8],\n 12: [2, 1],\n});\n\n/**\n * Cents by which a five-limit just interval departs from its 12-TET tempering\n * (positive means the just interval is wider).\n *\n * @param semitoneClass Semitone class in [0, 12].\n * @returns The deviation in cents.\n * @throws If `semitoneClass` is not an integer in [0, 12].\n * @category Pitch & Intervals\n */\nexport function justDeviationCents(semitoneClass: number): number {\n assertInteger(semitoneClass, 'semitone class', 0, 12);\n const ratio = JUST_RATIOS[semitoneClass];\n if (!ratio) {\n throw new InvalidInputError(`semitone class has no just ratio: ${semitoneClass}`);\n }\n return ratioToCents(ratio[0], ratio[1]) - semitoneClass * 100;\n}\n"],"mappings":"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","names":[]}
|