@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
|
@@ -0,0 +1,1242 @@
|
|
|
1
|
+
import { d as KeyScale, f as Note, h as NoteLike, i as NoteNameOptions, P as ParseResult } from './index-B9MNZboQ.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Anything that names a key: a key name, a plain {@link KeyScale}, or a value
|
|
5
|
+
* that serializes to one such as the `Key` class.
|
|
6
|
+
*
|
|
7
|
+
* @category Scales
|
|
8
|
+
*/
|
|
9
|
+
type KeyLike = string | KeyScale | {
|
|
10
|
+
/** The key data this value stands for, carrying the scale it denotes. */
|
|
11
|
+
toJSON(): {
|
|
12
|
+
scale: KeyScale;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Resolve any key-shaped value to a plain {@link KeyScale}.
|
|
17
|
+
*
|
|
18
|
+
* The counterpart of {@link toSpelledInterval} for keys: an entry point takes
|
|
19
|
+
* whatever form the caller has — the name a key field holds, the scale the
|
|
20
|
+
* theory module returns, or a `Key` instance — and gets one shape back. An
|
|
21
|
+
* instance is accepted through its `toJSON` method rather than by its type, so
|
|
22
|
+
* the layers below the model can read a class without importing it.
|
|
23
|
+
*
|
|
24
|
+
* A name is read by {@link parseKeyName} and nothing else, so every entry point
|
|
25
|
+
* that takes a key accepts exactly the names a key field already accepts, in
|
|
26
|
+
* every notation system it already reads. The mode word decides the scale: a
|
|
27
|
+
* major name gives the major scale, a minor name the natural minor. The
|
|
28
|
+
* harmonic and melodic forms have no name of their own, so a caller who needs
|
|
29
|
+
* one builds it with {@link scaleByName} and passes the scale.
|
|
30
|
+
*
|
|
31
|
+
* @param value A key name, a plain key/scale, or a value whose `toJSON`
|
|
32
|
+
* returns data carrying one.
|
|
33
|
+
* @returns The validated key/scale, with its root reduced to a pitch class.
|
|
34
|
+
* @throws If the value names no key, or its mode mask is out of range or
|
|
35
|
+
* excludes its own root.
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { toKeyScale } from '@libraz/libcantus';
|
|
39
|
+
* toKeyScale('C major').rootPc; // 0
|
|
40
|
+
* toKeyScale('A minor').rootPc; // 9
|
|
41
|
+
* ```
|
|
42
|
+
* @category Scales
|
|
43
|
+
*/
|
|
44
|
+
declare function toKeyScale(value: KeyLike): KeyScale;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* How keys stand to one another: the relative, parallel, dominant and
|
|
48
|
+
* subdominant keys, the enharmonic respelling of a key, and the relation that
|
|
49
|
+
* holds between two given keys.
|
|
50
|
+
*
|
|
51
|
+
* Every relation but {@link parallelKeyOf} is computed in fifths space, on top
|
|
52
|
+
* of {@link keySignatureFifths} and {@link keyFromFifths}. Stepping around the
|
|
53
|
+
* circle of fifths and reading the tonic back off it is what keeps the spelling
|
|
54
|
+
* right: the relative of Db major comes out as Bb minor, not A# minor.
|
|
55
|
+
*
|
|
56
|
+
* A relation always returns a plain major or minor key, because those are the
|
|
57
|
+
* only keys these relations name; {@link enharmonicKeyOf} is the exception that
|
|
58
|
+
* proves it, since a respelling is the same key rather than another one and so
|
|
59
|
+
* keeps the mask it was handed. A key that is not a plain major or minor —
|
|
60
|
+
* a church mode, harmonic minor, a pentatonic, an octatonic — is read through
|
|
61
|
+
* the mode its third names, and the fifths are counted from where its tonic
|
|
62
|
+
* stands on the circle rather than from the key's own signature: a mode's
|
|
63
|
+
* signature carries its own offset (D dorian is two fifths flatter than D
|
|
64
|
+
* major), which {@link keyFromFifths} does not know how to give back.
|
|
65
|
+
* {@link parallelKeyOf} travels no fifths at all, so it keeps the tonic
|
|
66
|
+
* spelling it was given.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A key paired with the spelled tonic that anchors its letter names.
|
|
71
|
+
*
|
|
72
|
+
* The tonic carries no octave: a key is rooted on a pitch class, and its
|
|
73
|
+
* spelling is what decides whether the key is written Db major or C# major.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import type { SpelledKey } from '@libraz/libcantus';
|
|
78
|
+
* import { majorKey, parseNote } from '@libraz/libcantus';
|
|
79
|
+
* const dbMajor: SpelledKey = { tonic: parseNote('Db'), key: majorKey(1) };
|
|
80
|
+
* ```
|
|
81
|
+
* @category Scales
|
|
82
|
+
*/
|
|
83
|
+
type SpelledKey = {
|
|
84
|
+
tonic: Note;
|
|
85
|
+
key: KeyScale;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* How one key stands to another, as reported by {@link keyRelationBetween} and
|
|
89
|
+
* tagged onto every entry of {@link relatedKeysOf}.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* import type { KeyRelation } from '@libraz/libcantus';
|
|
94
|
+
* const relation: KeyRelation = 'relative'; // C major -> A minor
|
|
95
|
+
* ```
|
|
96
|
+
* @category Scales
|
|
97
|
+
*/
|
|
98
|
+
type KeyRelation = 'same' | 'enharmonic' | 'relative' | 'parallel' | 'dominant' | 'subdominant' | 'relativeOfDominant' | 'relativeOfSubdominant';
|
|
99
|
+
/**
|
|
100
|
+
* Spell a key's tonic the conventional way: the letter and accidental the key
|
|
101
|
+
* is written with, paired with the key exactly as given.
|
|
102
|
+
*
|
|
103
|
+
* The candidates are the tonics keys are actually written on — everything from
|
|
104
|
+
* Cb to A#, which names each pitch class once or twice (C, but Db and C#) — and
|
|
105
|
+
* which of them names the key depends on whether the key has a signature of its
|
|
106
|
+
* own. A major or minor key, a church mode, and the harmonic and melodic minor
|
|
107
|
+
* are spelled on the tonic their signature is written on: pitch class 1 in
|
|
108
|
+
* major is Db, five flats, rather than C#, seven sharps; pitch class 8 in minor
|
|
109
|
+
* is G#, five sharps, rather than Ab, seven flats; and G# harmonic minor writes
|
|
110
|
+
* its raised seventh F## as an accidental over that signature rather than
|
|
111
|
+
* respelling the key. Where two signatures are equally long — pitch class 6 in
|
|
112
|
+
* major is F# at +6 and Gb at -6 — the flat side is taken.
|
|
113
|
+
*
|
|
114
|
+
* Every other scale only borrows the signature of its parallel major or minor,
|
|
115
|
+
* so nothing follows from it and the scale is spelled on the tonic it reads
|
|
116
|
+
* best from: the fewest double accidentals first, then the fewest accidentals
|
|
117
|
+
* altogether, and only then the shorter signature. That is why pitch class 1
|
|
118
|
+
* altered is C# — C# D E F G A B — rather than Db, which would have to write
|
|
119
|
+
* four double flats to say the same thing, and why pitch class 8 minor
|
|
120
|
+
* pentatonic is G# B C# D# F# rather than Ab B Db Eb Gb, which reads its third
|
|
121
|
+
* as an augmented second.
|
|
122
|
+
*
|
|
123
|
+
* The mask's third picks the mode the signature is measured in — a minor third
|
|
124
|
+
* and no major third reads as minor, anything else as major — and the key is
|
|
125
|
+
* returned unchanged, so a scale keeps its own mask and only borrows a tonic
|
|
126
|
+
* spelling. A caller that has a tonic spelling of its own always keeps it: this
|
|
127
|
+
* only answers for a key that arrives as a bare pitch class.
|
|
128
|
+
*
|
|
129
|
+
* Every 12-bit mask and every integer root is answered — a root outside [0, 11]
|
|
130
|
+
* is reduced first, and a mask with no third at all counts as major.
|
|
131
|
+
*
|
|
132
|
+
* The key is paired with the answer exactly as it was handed in, root and mask
|
|
133
|
+
* untouched, so this takes a plain key/scale rather than the wider forms the
|
|
134
|
+
* relations below accept: a coerced key could not be given back verbatim.
|
|
135
|
+
*
|
|
136
|
+
* @param key The key/scale to spell.
|
|
137
|
+
* @returns The conventional tonic spelling, paired with `key` verbatim.
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* import { formatNote, majorKey, scaleByName, spelledKeyOf } from '@libraz/libcantus';
|
|
141
|
+
* formatNote(spelledKeyOf(majorKey(1)).tonic); // 'Db'
|
|
142
|
+
* formatNote(spelledKeyOf(scaleByName('harmonicMinor', 8)).tonic); // 'G#'
|
|
143
|
+
* formatNote(spelledKeyOf(scaleByName('altered', 1)).tonic); // 'C#'
|
|
144
|
+
* ```
|
|
145
|
+
* @category Scales
|
|
146
|
+
*/
|
|
147
|
+
declare function spelledKeyOf(key: KeyScale): SpelledKey;
|
|
148
|
+
/**
|
|
149
|
+
* The relative key: the same key signature read in the other mode.
|
|
150
|
+
*
|
|
151
|
+
* C major and A minor share a signature, and so are each other's relative; the
|
|
152
|
+
* relation is its own inverse for a plain major or minor key. Because the new
|
|
153
|
+
* tonic is read off the circle of fifths rather than transposed by semitones,
|
|
154
|
+
* the relative of Db major is Bb minor and not its enharmonic A# minor.
|
|
155
|
+
*
|
|
156
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
157
|
+
* `Note`.
|
|
158
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
|
|
159
|
+
* decides which mode the relative is in.
|
|
160
|
+
* @returns The relative key and the tonic spelling it is written with.
|
|
161
|
+
* @throws If the tonic or the mask is malformed, or if the key's signature
|
|
162
|
+
* falls outside [-12, 12] fifths.
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* import { formatNote, majorKey, parseNote, relativeKeyOf } from '@libraz/libcantus';
|
|
166
|
+
* formatNote(relativeKeyOf(parseNote('C'), majorKey(0)).tonic); // 'A'
|
|
167
|
+
* formatNote(relativeKeyOf('Db', majorKey(1)).tonic); // 'Bb'
|
|
168
|
+
* ```
|
|
169
|
+
* @category Scales
|
|
170
|
+
*/
|
|
171
|
+
declare function relativeKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
|
|
172
|
+
/**
|
|
173
|
+
* The parallel key: the other mode on the same tonic.
|
|
174
|
+
*
|
|
175
|
+
* The tonic keeps the spelling it was given — the parallel minor of C major is
|
|
176
|
+
* C minor, never B# minor — which is why this is the one relation here that
|
|
177
|
+
* does not travel the circle of fifths.
|
|
178
|
+
*
|
|
179
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
180
|
+
* `Note`; its letter and alteration are kept.
|
|
181
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
|
|
182
|
+
* decides which mode the parallel is in.
|
|
183
|
+
* @returns The parallel key on the same tonic.
|
|
184
|
+
* @throws If the tonic or the mask is malformed.
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* import { formatNote, majorKey, parallelKeyOf, parseNote } from '@libraz/libcantus';
|
|
188
|
+
* const parallel = parallelKeyOf(parseNote('C'), majorKey(0));
|
|
189
|
+
* formatNote(parallel.tonic); // 'C'
|
|
190
|
+
* ```
|
|
191
|
+
* @category Scales
|
|
192
|
+
*/
|
|
193
|
+
declare function parallelKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
|
|
194
|
+
/**
|
|
195
|
+
* The dominant key: the key a fifth above, in the mode this key's third names.
|
|
196
|
+
*
|
|
197
|
+
* The fifth is measured from the tonic, so a key that is not a plain major or
|
|
198
|
+
* minor answers with the same move: the dominant of G mixolydian is D major and
|
|
199
|
+
* the dominant of D dorian is A minor.
|
|
200
|
+
*
|
|
201
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
202
|
+
* `Note`.
|
|
203
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
|
|
204
|
+
* decides the mode of the result.
|
|
205
|
+
* @returns The key a fifth above, in the mode this key's third names.
|
|
206
|
+
* @throws If the tonic or the mask is malformed, or if the resulting signature
|
|
207
|
+
* falls outside [-12, 12] fifths — the dominant of a key already at +12.
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* import { dominantKeyOf, formatNote, majorKey, parseNote } from '@libraz/libcantus';
|
|
211
|
+
* formatNote(dominantKeyOf(parseNote('C'), majorKey(0)).tonic); // 'G'
|
|
212
|
+
* ```
|
|
213
|
+
* @category Scales
|
|
214
|
+
*/
|
|
215
|
+
declare function dominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
|
|
216
|
+
/**
|
|
217
|
+
* The subdominant key: the key a fifth below, in the mode this key's third
|
|
218
|
+
* names, and the exact inverse of {@link dominantKeyOf} on the tonic spelling.
|
|
219
|
+
*
|
|
220
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
221
|
+
* `Note`.
|
|
222
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
|
|
223
|
+
* decides the mode of the result.
|
|
224
|
+
* @returns The key a fifth below, in the mode this key's third names.
|
|
225
|
+
* @throws If the tonic or the mask is malformed, or if the resulting signature
|
|
226
|
+
* falls outside [-12, 12] fifths — the subdominant of a key already at -12.
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* import { formatNote, majorKey, parseNote, subdominantKeyOf } from '@libraz/libcantus';
|
|
230
|
+
* formatNote(subdominantKeyOf(parseNote('C'), majorKey(0)).tonic); // 'F'
|
|
231
|
+
* ```
|
|
232
|
+
* @category Scales
|
|
233
|
+
*/
|
|
234
|
+
declare function subdominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
|
|
235
|
+
/**
|
|
236
|
+
* The same sounding key written the other way round the circle of fifths, or
|
|
237
|
+
* null when it has no second spelling.
|
|
238
|
+
*
|
|
239
|
+
* Twelve fifths is one enharmonic turn of the circle, so a tonic's alternative
|
|
240
|
+
* spelling sits at its own position ±12: Db (-5) is also C# (+7). It is offered
|
|
241
|
+
* only when the key is actually written there, by the same test
|
|
242
|
+
* {@link spelledKeyOf} spells with. That is why C major is alone while Db major
|
|
243
|
+
* and F# major each answer with their twin: Eb major's twin would be D# major
|
|
244
|
+
* and its nine sharps, and pitch class 3 altered is written D# but never Eb,
|
|
245
|
+
* which would need two double flats to say the same thing.
|
|
246
|
+
*
|
|
247
|
+
* The answer is therefore always a spelling the key is written on, which makes
|
|
248
|
+
* the relation its own inverse for such a key. A key spelled some other way —
|
|
249
|
+
* a tonic a transposition drove out to Ebb, say — still names its written twin,
|
|
250
|
+
* so this is also the way back from a spelling no one writes.
|
|
251
|
+
*
|
|
252
|
+
* The relation respells rather than moves, so the mask is kept: the other
|
|
253
|
+
* spelling of C# lydian is Db lydian, and its root pitch class is the one it
|
|
254
|
+
* was handed.
|
|
255
|
+
*
|
|
256
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
257
|
+
* `Note`.
|
|
258
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
|
|
259
|
+
* decides the mode the tonic is read in, and is carried into the result.
|
|
260
|
+
* @returns The other spelling of the key, or null when it is not written.
|
|
261
|
+
* @throws If the tonic or the mask is malformed.
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* import { enharmonicKeyOf, formatNote, majorKey, parseNote } from '@libraz/libcantus';
|
|
265
|
+
* const other = enharmonicKeyOf(parseNote('Db'), majorKey(1));
|
|
266
|
+
* other === null ? 'none' : formatNote(other.tonic); // 'C#'
|
|
267
|
+
* enharmonicKeyOf('C', majorKey(0)); // null
|
|
268
|
+
* ```
|
|
269
|
+
* @category Scales
|
|
270
|
+
*/
|
|
271
|
+
declare function enharmonicKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey | null;
|
|
272
|
+
/**
|
|
273
|
+
* The six closely related keys of a key, each tagged with its relation.
|
|
274
|
+
*
|
|
275
|
+
* These are the keys the German and Japanese teaching tradition counts as a
|
|
276
|
+
* key's near relations: the relative and parallel keys, the dominant and the
|
|
277
|
+
* subdominant, and the relatives of those two. For C major they are A minor,
|
|
278
|
+
* C minor, G major, F major, E minor and D minor, in that order. Five of them
|
|
279
|
+
* are written within one accidental of the key's own signature — the relative
|
|
280
|
+
* shares it exactly and the other four stand one away; the parallel key does
|
|
281
|
+
* not — C minor is three flats away from C major — and belongs to the set for
|
|
282
|
+
* the tonic it shares rather than for the signature it carries.
|
|
283
|
+
*
|
|
284
|
+
* @param tonic The spelled tonic of the key, as a note name, note data, or a
|
|
285
|
+
* `Note`.
|
|
286
|
+
* @param key The key/scale, as a key name, a key/scale, or a `Key`.
|
|
287
|
+
* @returns The six related keys, in relation order, each with its tonic
|
|
288
|
+
* spelling and its `relation` tag.
|
|
289
|
+
* @throws If the tonic or the mask is malformed, or if a neighbouring signature
|
|
290
|
+
* falls outside [-12, 12] fifths.
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* import { majorKey, parseNote, relatedKeysOf } from '@libraz/libcantus';
|
|
294
|
+
* relatedKeysOf(parseNote('C'), majorKey(0)).map((related) => related.relation);
|
|
295
|
+
* // ['relative', 'parallel', 'dominant', 'subdominant', 'relativeOfDominant', 'relativeOfSubdominant']
|
|
296
|
+
* ```
|
|
297
|
+
* @category Scales
|
|
298
|
+
*/
|
|
299
|
+
declare function relatedKeysOf(tonic: NoteLike, key: KeyLike): (SpelledKey & {
|
|
300
|
+
relation: KeyRelation;
|
|
301
|
+
})[];
|
|
302
|
+
/**
|
|
303
|
+
* How the second key stands to the first, or null when they are not related.
|
|
304
|
+
*
|
|
305
|
+
* The relations are tested in a fixed order — identity, enharmonic spelling,
|
|
306
|
+
* then the six closely related keys in the order {@link relatedKeysOf} lists
|
|
307
|
+
* them — and the first match is reported. Only the identity test looks at the
|
|
308
|
+
* tonic spelling; every other test compares root pitch class and mode, so
|
|
309
|
+
* C# minor and Db minor both read as the relative of E major.
|
|
310
|
+
*
|
|
311
|
+
* Both keys are read through the mode their third names, exactly as
|
|
312
|
+
* {@link relatedKeysOf} reads the key it is given, so A harmonic minor stands
|
|
313
|
+
* to C major as A minor does whichever of the two is asked about. That is what
|
|
314
|
+
* makes the answer symmetric: if one key's related keys name the other, the
|
|
315
|
+
* relation between them is reported from either side.
|
|
316
|
+
*
|
|
317
|
+
* @param a The key the relation is measured from.
|
|
318
|
+
* @param b The key the relation is measured to.
|
|
319
|
+
* @returns The relation, or null when `b` is none of `a`'s related keys.
|
|
320
|
+
* @throws If either key is malformed, or if a neighbouring signature of `a`
|
|
321
|
+
* falls outside [-12, 12] fifths.
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* import { keyRelationBetween, majorKey, minorKey, parseNote } from '@libraz/libcantus';
|
|
325
|
+
* const cMajor = { tonic: parseNote('C'), key: majorKey(0) };
|
|
326
|
+
* keyRelationBetween(cMajor, { tonic: parseNote('A'), key: minorKey(9) }); // 'relative'
|
|
327
|
+
* keyRelationBetween(cMajor, { tonic: parseNote('Eb'), key: minorKey(3) }); // null
|
|
328
|
+
* ```
|
|
329
|
+
* @category Scales
|
|
330
|
+
*/
|
|
331
|
+
declare function keyRelationBetween(a: SpelledKey, b: SpelledKey): KeyRelation | null;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The structured reading of a chord: what it is built from, not what it is
|
|
335
|
+
* called.
|
|
336
|
+
*
|
|
337
|
+
* A quality name is a closed vocabulary and a lead sheet's harmony is not. A
|
|
338
|
+
* chart writes a *set* of alterations over a base — `maj7#11`, `7b9#11`,
|
|
339
|
+
* `sus4(add9)` — so an enumeration of names can never be complete, and every
|
|
340
|
+
* name added to one is a name missing from its neighbours. A {@link ChordSpec}
|
|
341
|
+
* names the parts instead: a base triad, an optional seventh, altered, added
|
|
342
|
+
* and omitted degrees. A combination nothing names still has exact intervals,
|
|
343
|
+
* an exact pitch-class set, and a symbol that reads back as itself.
|
|
344
|
+
*
|
|
345
|
+
* Each {@link ChordQuality} is an alias for one spec, so the two directions
|
|
346
|
+
* cannot drift: a named chord's interval template is derived from its spec
|
|
347
|
+
* rather than listed beside it.
|
|
348
|
+
*/
|
|
349
|
+
/**
|
|
350
|
+
* Chord quality identifiers understood by the chord builder.
|
|
351
|
+
*
|
|
352
|
+
* Each name is an alias for one {@link ChordSpec}. The union is closed, so it
|
|
353
|
+
* names the chords a chart writes most often rather than every chord that can
|
|
354
|
+
* be written; build the rest from a spec.
|
|
355
|
+
*
|
|
356
|
+
* @category Chords
|
|
357
|
+
*/
|
|
358
|
+
type ChordQuality = 'maj' | 'min' | 'dim' | 'aug' | 'maj7' | 'min7' | 'dom7' | 'dim7' | 'm7b5' | 'minMaj7' | 'minMaj9' | 'minMaj11' | 'minMaj13' | 'aug7' | 'augMaj7' | 'majb5' | '6' | 'min6' | '6/9' | 'sus2' | 'sus4' | 'add9' | 'add11' | 'maj9' | 'min9' | 'dom9' | '7b9' | '7#9' | '7#11' | '7b13' | '11' | '13' | '5' | '7sus4' | '7b5' | '7alt' | '13b9' | 'maj13' | 'maj7#11' | 'min11' | 'min13' | 'minAdd9' | 'min6/9';
|
|
359
|
+
/**
|
|
360
|
+
* The triad (or dyad) a chord is built on, before any seventh or tension.
|
|
361
|
+
*
|
|
362
|
+
* `sus2` and `sus4` replace the third rather than sitting beside it, and
|
|
363
|
+
* `power` is the bare root and fifth, so the base says which tones the chord
|
|
364
|
+
* has before anything is added to them.
|
|
365
|
+
*
|
|
366
|
+
* @category Chords
|
|
367
|
+
*/
|
|
368
|
+
type ChordBase = 'maj' | 'min' | 'dim' | 'aug' | 'sus2' | 'sus4' | 'power';
|
|
369
|
+
/**
|
|
370
|
+
* The seventh stacked over a base: major, minor, or diminished.
|
|
371
|
+
*
|
|
372
|
+
* @category Chords
|
|
373
|
+
*/
|
|
374
|
+
type ChordSeventh = 'maj7' | 'min7' | 'dim7';
|
|
375
|
+
/**
|
|
376
|
+
* A chord degree that can be raised or lowered: the fifth and the tensions.
|
|
377
|
+
*
|
|
378
|
+
* @category Chords
|
|
379
|
+
*/
|
|
380
|
+
type AlteredDegree = 5 | 9 | 11 | 13;
|
|
381
|
+
/**
|
|
382
|
+
* One altered degree: which one, and by how much.
|
|
383
|
+
*
|
|
384
|
+
* `alter: 0` is not redundant — it says the degree is *present* and unaltered,
|
|
385
|
+
* which is what the natural ninth of a thirteenth chord is.
|
|
386
|
+
*
|
|
387
|
+
* @category Chords
|
|
388
|
+
*/
|
|
389
|
+
type Alteration = {
|
|
390
|
+
degree: AlteredDegree;
|
|
391
|
+
alter: -1 | 0 | 1;
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* A chord as its parts: base, seventh, alterations, additions and omissions.
|
|
395
|
+
*
|
|
396
|
+
* The library's structural chord model. `alterations` are tensions over a
|
|
397
|
+
* seventh chord; `additions` are degrees added without one (`add9`, and the
|
|
398
|
+
* sixth of a sixth chord); `omissions` are degrees the chord leaves out (the
|
|
399
|
+
* third of an eleventh chord, the fifth of a shell voicing) named by chord
|
|
400
|
+
* degree, so `omissions: [5]` is a fifth-less chord.
|
|
401
|
+
*
|
|
402
|
+
* @category Chords
|
|
403
|
+
*/
|
|
404
|
+
type ChordSpec = {
|
|
405
|
+
rootPc: number;
|
|
406
|
+
base: ChordBase;
|
|
407
|
+
seventh?: ChordSeventh;
|
|
408
|
+
alterations: Alteration[];
|
|
409
|
+
additions: number[];
|
|
410
|
+
omissions: number[];
|
|
411
|
+
bassPc?: number;
|
|
412
|
+
};
|
|
413
|
+
/**
|
|
414
|
+
* The semitone offsets above the root a spec sounds, ascending.
|
|
415
|
+
*
|
|
416
|
+
* The library's single derivation of chord tones: a name contributes nothing
|
|
417
|
+
* here, so a combination of alterations no name covers yields its intervals the
|
|
418
|
+
* same way a `maj7` does.
|
|
419
|
+
*
|
|
420
|
+
* @param spec The chord spec.
|
|
421
|
+
* @returns Semitone offsets above the root, ascending and deduplicated.
|
|
422
|
+
* @throws If any part of the spec is not one the model defines
|
|
423
|
+
* ({@link InvalidInputError}), exactly as {@link chordFromSpec} rejects it.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* import { chordSpecIntervals } from '@libraz/libcantus';
|
|
428
|
+
* chordSpecIntervals({
|
|
429
|
+
* rootPc: 0,
|
|
430
|
+
* base: 'maj',
|
|
431
|
+
* seventh: 'min7',
|
|
432
|
+
* alterations: [{ degree: 9, alter: -1 }, { degree: 11, alter: 1 }],
|
|
433
|
+
* additions: [],
|
|
434
|
+
* omissions: [],
|
|
435
|
+
* });
|
|
436
|
+
* // [0, 4, 7, 10, 13, 18] — the C7(b9,#11) no quality name covers
|
|
437
|
+
* ```
|
|
438
|
+
*
|
|
439
|
+
* @category Chords
|
|
440
|
+
*/
|
|
441
|
+
declare function chordSpecIntervals(spec: ChordSpec): number[];
|
|
442
|
+
/**
|
|
443
|
+
* The quality name a spec is reported under.
|
|
444
|
+
*
|
|
445
|
+
* A spec no name covers still has to answer, because `quality` is a required
|
|
446
|
+
* field of every {@link Chord} and of the records that travel with one. The
|
|
447
|
+
* answer is the *nearest* name: the largest named chord whose every tone this
|
|
448
|
+
* spec also sounds, and failing that the name of its base. Nothing is lost by
|
|
449
|
+
* it — the spec and the interval template still carry the alterations, and a
|
|
450
|
+
* symbol is written from those rather than from this name.
|
|
451
|
+
*
|
|
452
|
+
* @param spec The chord spec.
|
|
453
|
+
* @returns The name that fits the spec exactly, or the nearest one that fits
|
|
454
|
+
* inside it.
|
|
455
|
+
* @throws If any part of the spec is not one the model defines
|
|
456
|
+
* ({@link InvalidInputError}), exactly as {@link chordFromSpec} rejects it.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* import { chordSpecQuality, parseChordSymbol } from '@libraz/libcantus';
|
|
461
|
+
* parseChordSymbol('C7(b9,#11)').quality; // '7b9' — the nearest name
|
|
462
|
+
* chordSpecQuality({ rootPc: 0, base: 'maj', seventh: 'min7', alterations: [],
|
|
463
|
+
* additions: [], omissions: [] }); // 'dom7'
|
|
464
|
+
* ```
|
|
465
|
+
*
|
|
466
|
+
* @category Chords
|
|
467
|
+
*/
|
|
468
|
+
declare function chordSpecQuality(spec: ChordSpec): ChordQuality;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* The basic role a chord tone plays: the degrees a chord's own template names.
|
|
472
|
+
*
|
|
473
|
+
* The shared vocabulary behind {@link chordToneRole}, the harmony layer's
|
|
474
|
+
* `HarmonyRole`, and the voice analyser's labels, so the three cannot drift.
|
|
475
|
+
*
|
|
476
|
+
* @category Chords
|
|
477
|
+
*/
|
|
478
|
+
type ChordToneRole = 'root' | 'third' | 'fifth' | 'sixth' | 'seventh';
|
|
479
|
+
/**
|
|
480
|
+
* A bare spelled pitch used as an enharmonic hint: a diatonic letter
|
|
481
|
+
* (0..6 = C..B) plus a chromatic alteration (-2 double-flat .. +2 double-sharp).
|
|
482
|
+
* Mirrors the letter/alter half of the pitch module's `Note` without an octave.
|
|
483
|
+
*
|
|
484
|
+
* @category Chords
|
|
485
|
+
*/
|
|
486
|
+
type PitchSpelling = {
|
|
487
|
+
letter: number;
|
|
488
|
+
alter: number;
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* A chord expressed as a root pitch class plus semitone offsets.
|
|
492
|
+
*
|
|
493
|
+
* `intervals` is what the chord sounds and `quality` is what it is called. The
|
|
494
|
+
* two are not equally expressive: a chart writes any set of alterations over a
|
|
495
|
+
* base, while {@link ChordQuality} is a closed list of names, so `quality` is a
|
|
496
|
+
* *derived* label — the name that fits the chord exactly, or else the nearest
|
|
497
|
+
* name inside it (see {@link chordSpecQuality}). Read the structure with
|
|
498
|
+
* {@link chordSpecOf} rather than the name when the alterations matter; that is
|
|
499
|
+
* also what {@link formatChordSymbol} does, so a chord no name covers still
|
|
500
|
+
* writes itself out in full.
|
|
501
|
+
*
|
|
502
|
+
* `rootSpelling`/`bassSpelling` are optional enharmonic hints recorded by
|
|
503
|
+
* parsers (e.g. `parseChordSymbol('Bb7')`) so formatters can reproduce the
|
|
504
|
+
* original spelling instead of defaulting to sharps. Consumers may ignore
|
|
505
|
+
* them; a hint is only trusted when its pitch class still matches the
|
|
506
|
+
* corresponding `rootPc`/`bassPc`.
|
|
507
|
+
*
|
|
508
|
+
* `toneSpellings` is the same kind of hint for the tones themselves, one
|
|
509
|
+
* spelling per entry in `intervals`. It exists for the chords whose letters do
|
|
510
|
+
* not follow from stacking thirds: an augmented sixth is ten semitones like a
|
|
511
|
+
* minor seventh but is five letters above its root rather than six, and no
|
|
512
|
+
* {@link ChordQuality} can say so. Builders that know better attach it, and
|
|
513
|
+
* {@link spellChordFromRoot} reads the letter distances back out of it; a hint
|
|
514
|
+
* is trusted as a whole or not at all, and only while every spelling still
|
|
515
|
+
* names the pitch class its interval names.
|
|
516
|
+
*
|
|
517
|
+
* @category Chords
|
|
518
|
+
*/
|
|
519
|
+
type Chord = {
|
|
520
|
+
rootPc: number;
|
|
521
|
+
quality: ChordQuality;
|
|
522
|
+
intervals: number[];
|
|
523
|
+
bassPc?: number;
|
|
524
|
+
rootSpelling?: PitchSpelling;
|
|
525
|
+
bassSpelling?: PitchSpelling;
|
|
526
|
+
toneSpellings?: PitchSpelling[];
|
|
527
|
+
};
|
|
528
|
+
/**
|
|
529
|
+
* A chord positioned in time: a root pitch class and quality placed at a beat,
|
|
530
|
+
* with optional harmonic-analysis hints. Serves as the neutral exchange record
|
|
531
|
+
* between progression generators and timeline analysers.
|
|
532
|
+
*
|
|
533
|
+
* @category Composition
|
|
534
|
+
*/
|
|
535
|
+
type ChordSpan = {
|
|
536
|
+
rootPc: number;
|
|
537
|
+
quality: ChordQuality;
|
|
538
|
+
startBeat: number;
|
|
539
|
+
bassPc?: number;
|
|
540
|
+
/**
|
|
541
|
+
* Semitone offsets from the root, for a chord the {@link ChordQuality} union
|
|
542
|
+
* cannot name exactly. Omitted for a standard chord: the template is then
|
|
543
|
+
* derived from `quality`, which is what every producer and consumer did
|
|
544
|
+
* before the field existed. Read a span with {@link chordFromSpan} so either
|
|
545
|
+
* case yields the same chord.
|
|
546
|
+
*/
|
|
547
|
+
intervals?: number[];
|
|
548
|
+
/** 1-based scale degree of the chord root, when known. */
|
|
549
|
+
degree?: number;
|
|
550
|
+
/** True when the chord is a secondary dominant tonicizing another degree. */
|
|
551
|
+
secondaryDominant?: boolean;
|
|
552
|
+
};
|
|
553
|
+
/**
|
|
554
|
+
* A chord occupying a half-open beat span `[startBeat, endBeat)`.
|
|
555
|
+
*
|
|
556
|
+
* The exchange record between anything that lays chords out in time — a
|
|
557
|
+
* timeline analysis, a bass-line plan, an accompaniment generator — so those
|
|
558
|
+
* surfaces share one definition instead of agreeing by coincidence.
|
|
559
|
+
*
|
|
560
|
+
* @category Composition
|
|
561
|
+
*/
|
|
562
|
+
type ChordSegment = {
|
|
563
|
+
startBeat: number;
|
|
564
|
+
endBeat: number;
|
|
565
|
+
chord: Chord;
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* All chord qualities the builder understands, in declaration order.
|
|
569
|
+
*
|
|
570
|
+
* The order is stable but carries no meaning: it is neither alphabetical nor
|
|
571
|
+
* sorted by template size. {@link detectChord} uses it as its final tie-break,
|
|
572
|
+
* so it is part of the observable behaviour and does not get reshuffled
|
|
573
|
+
* casually — but do not read it as a ranking.
|
|
574
|
+
*
|
|
575
|
+
* @category Chords
|
|
576
|
+
*/
|
|
577
|
+
declare function chordQualities(): ChordQuality[];
|
|
578
|
+
/**
|
|
579
|
+
* Build a chord rooted on a diatonic scale degree.
|
|
580
|
+
*
|
|
581
|
+
* The root pitch class is the degree's diatonic pitch class in `key`; the
|
|
582
|
+
* quality's interval template is attached unchanged. Degrees beyond the scale
|
|
583
|
+
* length wrap around, so degree 8 of a heptatonic scale is its degree 1.
|
|
584
|
+
*
|
|
585
|
+
* @param degree 1-based scale degree of the chord root: 1 is the tonic.
|
|
586
|
+
* @param ext Chord quality to apply.
|
|
587
|
+
* @param key Key context supplying the diatonic root, as a key name, a
|
|
588
|
+
* key/scale, or a `Key`.
|
|
589
|
+
* @returns The constructed chord.
|
|
590
|
+
* @throws If `degree` is not a positive integer, or `ext` is not a known
|
|
591
|
+
* quality.
|
|
592
|
+
*
|
|
593
|
+
* @category Chords
|
|
594
|
+
*/
|
|
595
|
+
declare function chordFromDegree(degree: number, ext: ChordQuality, key: KeyLike): Chord;
|
|
596
|
+
/**
|
|
597
|
+
* Build a chord from an explicit root pitch class and quality.
|
|
598
|
+
*
|
|
599
|
+
* @param rootPc Root pitch class (0..11).
|
|
600
|
+
* @param quality Chord quality supplying the interval template.
|
|
601
|
+
* @param bassPc Optional slash-chord bass pitch class.
|
|
602
|
+
* @returns The constructed chord.
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```ts
|
|
606
|
+
* import { makeChord } from '@libraz/libcantus';
|
|
607
|
+
* const cmaj7 = makeChord(0, 'maj7');
|
|
608
|
+
* // { rootPc: 0, quality: 'maj7', intervals: [0, 4, 7, 11] }
|
|
609
|
+
* ```
|
|
610
|
+
*
|
|
611
|
+
* @category Chords
|
|
612
|
+
*/
|
|
613
|
+
declare function makeChord(rootPc: number, quality: ChordQuality, bassPc?: number): Chord;
|
|
614
|
+
/**
|
|
615
|
+
* Build a chord from a {@link ChordSpec}.
|
|
616
|
+
*
|
|
617
|
+
* The structural way in, for the chords a quality name cannot reach: the tones
|
|
618
|
+
* come from the spec, and `quality` is the name that fits it, or the nearest
|
|
619
|
+
* name when none does. A chord built this way is an ordinary {@link Chord} —
|
|
620
|
+
* every consumer reads its `intervals` as before, and
|
|
621
|
+
* {@link formatChordSymbol} writes the alterations back out.
|
|
622
|
+
*
|
|
623
|
+
* @param spec The chord spec; its `rootPc`/`bassPc` become the chord's.
|
|
624
|
+
* @returns The constructed chord.
|
|
625
|
+
* @throws If any part of the spec is not one the model defines.
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```ts
|
|
629
|
+
* import { chordFromSpec } from '@libraz/libcantus';
|
|
630
|
+
* chordFromSpec({
|
|
631
|
+
* rootPc: 0,
|
|
632
|
+
* base: 'maj',
|
|
633
|
+
* seventh: 'maj7',
|
|
634
|
+
* alterations: [{ degree: 11, alter: 1 }],
|
|
635
|
+
* additions: [],
|
|
636
|
+
* omissions: [],
|
|
637
|
+
* });
|
|
638
|
+
* // { rootPc: 0, quality: 'maj7#11', intervals: [0, 4, 7, 11, 18] }
|
|
639
|
+
* ```
|
|
640
|
+
*
|
|
641
|
+
* @category Chords
|
|
642
|
+
*/
|
|
643
|
+
declare function chordFromSpec(spec: ChordSpec): Chord;
|
|
644
|
+
/**
|
|
645
|
+
* Read a chord structurally: which base, seventh, alterations and omissions it
|
|
646
|
+
* is made of.
|
|
647
|
+
*
|
|
648
|
+
* The reading comes from the tones themselves, so it describes a chord whatever
|
|
649
|
+
* built it — a parsed symbol, a detected pitch set, a span carrying a template
|
|
650
|
+
* no name covers. Only a template with no structural reading at all falls back
|
|
651
|
+
* to the one its `quality` names.
|
|
652
|
+
*
|
|
653
|
+
* @param chord The chord to read.
|
|
654
|
+
* @returns The spec, carrying the chord's own root and slash bass.
|
|
655
|
+
* @throws If the chord's template has no reading and its quality is unknown.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* import { chordSpecOf, parseChordSymbol } from '@libraz/libcantus';
|
|
660
|
+
* chordSpecOf(parseChordSymbol('C7(b9,#11)')).alterations;
|
|
661
|
+
* // [{ degree: 9, alter: -1 }, { degree: 11, alter: 1 }]
|
|
662
|
+
* ```
|
|
663
|
+
*
|
|
664
|
+
* @category Chords
|
|
665
|
+
*/
|
|
666
|
+
declare function chordSpecOf(chord: Chord): ChordSpec;
|
|
667
|
+
/**
|
|
668
|
+
* Read a {@link ChordSpan} as a chord.
|
|
669
|
+
*
|
|
670
|
+
* The canonical way to cross from a span to a chord. A span carrying no
|
|
671
|
+
* `intervals` yields exactly the chord {@link makeChord} builds from its root,
|
|
672
|
+
* quality and bass; a span carrying an explicit template keeps it, so a color
|
|
673
|
+
* tone the {@link ChordQuality} union cannot name survives the trip through a
|
|
674
|
+
* progression, a timeline, or a generator. The template is copied, so the
|
|
675
|
+
* chord and the span never share an array.
|
|
676
|
+
*
|
|
677
|
+
* @param span The span to read.
|
|
678
|
+
* @returns The chord the span describes.
|
|
679
|
+
* @throws If the span's root, bass or quality is invalid, or its `intervals`
|
|
680
|
+
* are not a non-empty array of integer semitone offsets.
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* import { chordFromSpan } from '@libraz/libcantus';
|
|
685
|
+
* chordFromSpan({ rootPc: 0, quality: 'maj7', startBeat: 0 });
|
|
686
|
+
* // { rootPc: 0, quality: 'maj7', intervals: [0, 4, 7, 11] }
|
|
687
|
+
* chordFromSpan({ rootPc: 0, quality: 'maj7', startBeat: 0, intervals: [0, 4, 11, 18] });
|
|
688
|
+
* // the same span with a fifth-less, #11 voicing the quality cannot name
|
|
689
|
+
* ```
|
|
690
|
+
*
|
|
691
|
+
* @category Composition
|
|
692
|
+
*/
|
|
693
|
+
declare function chordFromSpan(span: ChordSpan): Chord;
|
|
694
|
+
/**
|
|
695
|
+
* Place a chord at a beat as a {@link ChordSpan}.
|
|
696
|
+
*
|
|
697
|
+
* The interval template is recorded only when it departs from the one the
|
|
698
|
+
* chord's quality names, so a span built from a standard chord carries the
|
|
699
|
+
* same fields it always did while a custom template is not lost.
|
|
700
|
+
*
|
|
701
|
+
* @param chord The chord to place.
|
|
702
|
+
* @param startBeat Beat the chord starts on.
|
|
703
|
+
* @returns The span describing that chord at that beat.
|
|
704
|
+
* @throws If `startBeat` is not finite, or the chord's quality is unknown.
|
|
705
|
+
*
|
|
706
|
+
* @example
|
|
707
|
+
* ```ts
|
|
708
|
+
* import { makeChord, spanFromChord } from '@libraz/libcantus';
|
|
709
|
+
* spanFromChord(makeChord(0, 'maj7'), 4);
|
|
710
|
+
* // { rootPc: 0, quality: 'maj7', startBeat: 4 } — no intervals for a standard chord
|
|
711
|
+
* ```
|
|
712
|
+
*
|
|
713
|
+
* @category Composition
|
|
714
|
+
*/
|
|
715
|
+
declare function spanFromChord(chord: Chord, startBeat: number): ChordSpan;
|
|
716
|
+
/**
|
|
717
|
+
* Transpose a chord by a number of semitones.
|
|
718
|
+
*
|
|
719
|
+
* The interval template is carried over untouched, so a chord whose intervals
|
|
720
|
+
* were customised keeps them, and a slash bass moves with the root. Enharmonic
|
|
721
|
+
* spelling hints are dropped rather than shifted: the letter a transposed root
|
|
722
|
+
* should take depends on the destination key, which a semitone count does not
|
|
723
|
+
* carry. Pass the result through {@link spellChord} to respell it.
|
|
724
|
+
*
|
|
725
|
+
* @param chord The chord to transpose.
|
|
726
|
+
* @param semitones The signed semitone offset.
|
|
727
|
+
* @returns The transposed chord.
|
|
728
|
+
* @throws If `semitones` is not finite.
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```ts
|
|
732
|
+
* import { formatChordSymbol, parseChordSymbol, transposeChord } from '@libraz/libcantus';
|
|
733
|
+
* formatChordSymbol(transposeChord(parseChordSymbol('C/G'), 2)); // 'D/A'
|
|
734
|
+
* ```
|
|
735
|
+
*
|
|
736
|
+
* @category Chords
|
|
737
|
+
*/
|
|
738
|
+
declare function transposeChord(chord: Chord, semitones: number): Chord;
|
|
739
|
+
/**
|
|
740
|
+
* Get the sorted, deduplicated pitch classes of a chord.
|
|
741
|
+
*
|
|
742
|
+
* A slash bass is one of them: `F/G` sounds a G, the voicers put it in the
|
|
743
|
+
* bass, and a set that omitted it would not re-detect as the chord it came
|
|
744
|
+
* from. Pass `includeBass: false` to enumerate the interval template alone.
|
|
745
|
+
*
|
|
746
|
+
* @param chord The chord to enumerate.
|
|
747
|
+
* @param opts Set `includeBass: false` to omit a slash bass.
|
|
748
|
+
* @returns The chord's pitch classes, sorted ascending in [0, 11].
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* import { makeChord, chordPitchClasses } from '@libraz/libcantus';
|
|
753
|
+
* chordPitchClasses(makeChord(0, 'maj7')); // [0, 4, 7, 11]
|
|
754
|
+
* chordPitchClasses(makeChord(5, 'maj', 7)); // [0, 5, 7, 9] — F/G, G included
|
|
755
|
+
* ```
|
|
756
|
+
*
|
|
757
|
+
* @category Chords
|
|
758
|
+
*/
|
|
759
|
+
declare function chordPitchClasses(chord: Chord, opts?: {
|
|
760
|
+
includeBass?: boolean;
|
|
761
|
+
}): number[];
|
|
762
|
+
/**
|
|
763
|
+
* A pitch's interval class above the chord root, in [0, 11].
|
|
764
|
+
*
|
|
765
|
+
* The shared reading of "where does this pitch sit in the chord", used by the
|
|
766
|
+
* safety evaluator and the voice analyser alike so they cannot disagree.
|
|
767
|
+
*
|
|
768
|
+
* @param pitch MIDI pitch or bare pitch class.
|
|
769
|
+
* @param chord The chord providing the root reference.
|
|
770
|
+
* @returns The interval class above the root.
|
|
771
|
+
* @category Chords
|
|
772
|
+
*/
|
|
773
|
+
declare function intervalAboveRoot(pitch: number, chord: Chord): number;
|
|
774
|
+
/**
|
|
775
|
+
* Whether a pitch is one of a chord's tones, ignoring octave.
|
|
776
|
+
*
|
|
777
|
+
* @param pitch MIDI pitch or bare pitch class.
|
|
778
|
+
* @param chord The chord, or null for no sounding harmony.
|
|
779
|
+
* @returns True when the pitch class belongs to the chord.
|
|
780
|
+
* @category Chords
|
|
781
|
+
*/
|
|
782
|
+
declare function isChordMember(pitch: number, chord: Chord | null): boolean;
|
|
783
|
+
/**
|
|
784
|
+
* Determine a pitch's harmonic role within a chord.
|
|
785
|
+
*
|
|
786
|
+
* The role is derived from the pitch's interval above the chord root, reduced
|
|
787
|
+
* modulo 12. A major sixth (9) reads as a `sixth` for sixth chords but as a
|
|
788
|
+
* `seventh` for a diminished-seventh chord; ninths and other tensions have no
|
|
789
|
+
* basic role and return null.
|
|
790
|
+
*
|
|
791
|
+
* @param pitch MIDI pitch or bare pitch class.
|
|
792
|
+
* @param chord The chord providing the root reference.
|
|
793
|
+
* @returns The chord-tone role, or null if the pitch has no basic role.
|
|
794
|
+
*
|
|
795
|
+
* @category Chords
|
|
796
|
+
*/
|
|
797
|
+
declare function chordToneRole(pitch: number, chord: Chord): ChordToneRole | null;
|
|
798
|
+
/**
|
|
799
|
+
* Build the diatonic triad rooted on a scale degree by stacking scale thirds.
|
|
800
|
+
*
|
|
801
|
+
* Unlike {@link chordFromDegree}, the chord quality is derived from the scale
|
|
802
|
+
* rather than supplied, so degrees yield their scale-correct triads (e.g. a
|
|
803
|
+
* diminished triad on the leading tone of a major key).
|
|
804
|
+
*
|
|
805
|
+
* @param degree 1-based scale degree of the chord root: 1 is the tonic.
|
|
806
|
+
* @param key Key/scale context, as a key name, a key/scale, or a `Key`.
|
|
807
|
+
* @returns The diatonic triad.
|
|
808
|
+
* @throws If the scale is not heptatonic. Stacking thirds means skipping every
|
|
809
|
+
* other degree, which only lands on a triad when there are seven of them, so
|
|
810
|
+
* the pentatonic, blues, whole-tone, octatonic and chromatic scales have no
|
|
811
|
+
* diatonic triad; use {@link chordFromDegree} with an explicit quality there.
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* ```ts
|
|
815
|
+
* import { majorKey, diatonicTriad } from '@libraz/libcantus';
|
|
816
|
+
* const tonic = diatonicTriad(1, majorKey(0)); // C major triad (degree 1 of C major)
|
|
817
|
+
* ```
|
|
818
|
+
*
|
|
819
|
+
* @category Chords
|
|
820
|
+
*/
|
|
821
|
+
declare function diatonicTriad(degree: number, key: KeyLike): Chord;
|
|
822
|
+
/**
|
|
823
|
+
* Build the diatonic seventh chord rooted on a scale degree.
|
|
824
|
+
*
|
|
825
|
+
* @param degree 1-based scale degree of the chord root: 1 is the tonic.
|
|
826
|
+
* @param key Key/scale context, as a key name, a key/scale, or a `Key`.
|
|
827
|
+
* @returns The diatonic seventh chord.
|
|
828
|
+
* @throws If the scale is not heptatonic, for the reason given on
|
|
829
|
+
* {@link diatonicTriad}.
|
|
830
|
+
*
|
|
831
|
+
* @category Chords
|
|
832
|
+
*/
|
|
833
|
+
declare function diatonicSeventh(degree: number, key: KeyLike): Chord;
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Style policy: `strict` counterpoint vs. `pop` voice leading.
|
|
837
|
+
*
|
|
838
|
+
* A profile says two things. It sets the severity of each reason
|
|
839
|
+
* {@link evaluateSafety} finds, which decides what is rejected outright, and it
|
|
840
|
+
* carries the {@link ProfileWeights} that rank whatever survives — because most
|
|
841
|
+
* of what separates the two styles is preference rather than prohibition.
|
|
842
|
+
*
|
|
843
|
+
* @category Arrangement & Analysis
|
|
844
|
+
*/
|
|
845
|
+
type SafetyProfile = 'strict' | 'pop';
|
|
846
|
+
/**
|
|
847
|
+
* How a profile ranks the candidates its severity table has let through.
|
|
848
|
+
*
|
|
849
|
+
* Each field is a score contribution, added when the situation it names occurs;
|
|
850
|
+
* a negative value is a penalty. The scale is arbitrary but shared, so only the
|
|
851
|
+
* relative sizes matter. Nothing here can make an unsafe pitch usable: rejection
|
|
852
|
+
* is the severity table's business, and these weights only order what is left.
|
|
853
|
+
*
|
|
854
|
+
* @category Arrangement & Analysis
|
|
855
|
+
*/
|
|
856
|
+
type ProfileWeights = {
|
|
857
|
+
/** A third or a sixth against the other voice. */
|
|
858
|
+
imperfectConsonance: number;
|
|
859
|
+
/** A unison, fifth or octave against the other voice. */
|
|
860
|
+
perfectConsonance: number;
|
|
861
|
+
/** A second, seventh or tritone against the other voice. */
|
|
862
|
+
dissonance: number;
|
|
863
|
+
/** The candidate belongs to the sounding chord. */
|
|
864
|
+
chordTone: number;
|
|
865
|
+
/** The two voices move in opposite directions. */
|
|
866
|
+
contraryMotion: number;
|
|
867
|
+
/** One voice holds while the other moves. */
|
|
868
|
+
obliqueMotion: number;
|
|
869
|
+
/** Both voices move the same way into a different interval. */
|
|
870
|
+
similarMotion: number;
|
|
871
|
+
/** Both voices move the same way, keeping the same imperfect consonance. */
|
|
872
|
+
parallelImperfect: number;
|
|
873
|
+
/** The candidate is a step from the previous note of its own line. */
|
|
874
|
+
stepwise: number;
|
|
875
|
+
/** Per semitone of melodic movement, so a leap costs more than a step. */
|
|
876
|
+
melodicDistance: number;
|
|
877
|
+
/** The move is a leap strict counterpoint forbids outright. */
|
|
878
|
+
forbiddenLeap: number;
|
|
879
|
+
/** Per semitone away from the centre of the line's register. */
|
|
880
|
+
registerDrift: number;
|
|
881
|
+
};
|
|
882
|
+
/**
|
|
883
|
+
* The weight table each profile ranks candidates by.
|
|
884
|
+
*
|
|
885
|
+
* `strict` states the sixteenth-century preferences: contrary motion is worth
|
|
886
|
+
* seeking, similar motion is worth avoiding, imperfect consonances carry the
|
|
887
|
+
* line, and a run of parallel thirds earns nothing. `pop` inverts two of those.
|
|
888
|
+
* Parallel thirds and sixths are the harmony line an arranger writes on purpose,
|
|
889
|
+
* a pedal or a held unison under a moving lead is a texture rather than a fault,
|
|
890
|
+
* and similar motion is simply how two parts often move together — while a bass
|
|
891
|
+
* moving against the lead is still worth preferring where it is available.
|
|
892
|
+
*
|
|
893
|
+
* @category Arrangement & Analysis
|
|
894
|
+
*/
|
|
895
|
+
declare const PROFILE_WEIGHTS: Readonly<Record<SafetyProfile, Readonly<ProfileWeights>>>;
|
|
896
|
+
/**
|
|
897
|
+
* Resolve a profile to the weights that rank its candidates, with per-field
|
|
898
|
+
* overrides applied on top.
|
|
899
|
+
*
|
|
900
|
+
* @param profile The profile whose table to start from.
|
|
901
|
+
* @param overrides Fields to replace; anything absent keeps the profile's value.
|
|
902
|
+
* @returns The complete weight table to score with.
|
|
903
|
+
* @throws If `profile` is not one of the profile names
|
|
904
|
+
* ({@link InvalidInputError}). A misspelled profile scored under a default
|
|
905
|
+
* table would rank candidates by a style the caller did not ask for.
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* import { profileWeights } from '@libraz/libcantus';
|
|
909
|
+
* profileWeights('pop', { parallelImperfect: 4 }).contraryMotion; // 1.5 — the rest is untouched
|
|
910
|
+
* ```
|
|
911
|
+
* @category Arrangement & Analysis
|
|
912
|
+
*/
|
|
913
|
+
declare function profileWeights(profile: SafetyProfile, overrides?: Partial<ProfileWeights>): ProfileWeights;
|
|
914
|
+
/**
|
|
915
|
+
* Overall placeability verdict for a candidate pitch.
|
|
916
|
+
*
|
|
917
|
+
* @category Arrangement & Analysis
|
|
918
|
+
*/
|
|
919
|
+
declare enum NoteSafety {
|
|
920
|
+
Safe = 0,
|
|
921
|
+
Warning = 1,
|
|
922
|
+
Dissonant = 2
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Bit flags describing why a pitch received its verdict.
|
|
926
|
+
*
|
|
927
|
+
* @category Arrangement & Analysis
|
|
928
|
+
*/
|
|
929
|
+
declare enum ReasonFlag {
|
|
930
|
+
ChordTone = 1,
|
|
931
|
+
Tension = 2,
|
|
932
|
+
AvoidNote = 4,
|
|
933
|
+
ScaleTone = 8,
|
|
934
|
+
NonScale = 16,
|
|
935
|
+
OutOfRange = 32,
|
|
936
|
+
Tritone = 64,
|
|
937
|
+
LargeLeap = 128,
|
|
938
|
+
MinorSecond = 256,
|
|
939
|
+
MajorSeventh = 512,
|
|
940
|
+
VerticalDissonance = 1024,
|
|
941
|
+
ParallelPerfect = 2048,
|
|
942
|
+
HiddenParallel = 4096,
|
|
943
|
+
VoiceCrossing = 8192,
|
|
944
|
+
Suspension = 16384,
|
|
945
|
+
NeedsResolution = 32768,
|
|
946
|
+
MelodicTritone = 65536
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* A neighbouring voice's current and previous pitch.
|
|
950
|
+
*
|
|
951
|
+
* @category Arrangement & Analysis
|
|
952
|
+
*/
|
|
953
|
+
type VoiceSnapshot = {
|
|
954
|
+
pitch: number;
|
|
955
|
+
prevPitch?: number;
|
|
956
|
+
};
|
|
957
|
+
/**
|
|
958
|
+
* Everything needed to evaluate one candidate pitch.
|
|
959
|
+
*
|
|
960
|
+
* @category Arrangement & Analysis
|
|
961
|
+
*/
|
|
962
|
+
type SafetyQuery = {
|
|
963
|
+
profile: SafetyProfile;
|
|
964
|
+
candidatePitch: number;
|
|
965
|
+
prevPitch?: number;
|
|
966
|
+
chord: Chord | null;
|
|
967
|
+
key: KeyScale;
|
|
968
|
+
otherVoices: VoiceSnapshot[];
|
|
969
|
+
strongBeat: boolean;
|
|
970
|
+
vocalLow?: number;
|
|
971
|
+
vocalHigh?: number;
|
|
972
|
+
};
|
|
973
|
+
/**
|
|
974
|
+
* The verdict, reason flags, and optional resolution guidance.
|
|
975
|
+
*
|
|
976
|
+
* @category Arrangement & Analysis
|
|
977
|
+
*/
|
|
978
|
+
type SafetyResult = {
|
|
979
|
+
safety: NoteSafety;
|
|
980
|
+
reasons: number;
|
|
981
|
+
resolveTo?: number;
|
|
982
|
+
suggestions?: number[];
|
|
983
|
+
rationale?: string;
|
|
984
|
+
};
|
|
985
|
+
/**
|
|
986
|
+
* Evaluate the safety of placing a candidate pitch in a voice.
|
|
987
|
+
*
|
|
988
|
+
* Reasons are merged from harmonic context (chord tone / tension / avoid / scale)
|
|
989
|
+
* and counterpoint (vertical dissonance, parallels, voice crossing). The `strict`
|
|
990
|
+
* profile treats avoid notes and parallels as dissonant; `pop` treats them as
|
|
991
|
+
* warnings. The final verdict is the worst severity among the reasons.
|
|
992
|
+
*
|
|
993
|
+
* @param q The candidate and its harmonic/voice-leading context.
|
|
994
|
+
* @returns The verdict, reason bitmask, and optional resolution guidance.
|
|
995
|
+
* @throws If the profile is not one of the profile names, or if any pitch in
|
|
996
|
+
* the query is not a MIDI pitch in 0..127 ({@link InvalidInputError}).
|
|
997
|
+
* @example
|
|
998
|
+
* ```ts
|
|
999
|
+
* import { evaluateSafety, makeChord, majorKey, NoteSafety } from '@libraz/libcantus';
|
|
1000
|
+
* const result = evaluateSafety({
|
|
1001
|
+
* profile: 'pop',
|
|
1002
|
+
* candidatePitch: 60, // C over a C major chord
|
|
1003
|
+
* chord: makeChord(0, 'maj'),
|
|
1004
|
+
* key: majorKey(0),
|
|
1005
|
+
* otherVoices: [],
|
|
1006
|
+
* strongBeat: true,
|
|
1007
|
+
* });
|
|
1008
|
+
* result.safety === NoteSafety.Safe; // true — C is a chord tone
|
|
1009
|
+
* ```
|
|
1010
|
+
* @category Arrangement & Analysis
|
|
1011
|
+
*/
|
|
1012
|
+
declare function evaluateSafety(q: SafetyQuery, opts?: EvaluateSafetyOptions): SafetyResult;
|
|
1013
|
+
/**
|
|
1014
|
+
* Options for {@link evaluateSafety}.
|
|
1015
|
+
*
|
|
1016
|
+
* @category Arrangement & Analysis
|
|
1017
|
+
*/
|
|
1018
|
+
type EvaluateSafetyOptions = {
|
|
1019
|
+
/**
|
|
1020
|
+
* Whether to search for safe replacement pitches when the verdict is not
|
|
1021
|
+
* safe. Turning it off skips up to two dozen extra evaluations per rejected
|
|
1022
|
+
* pitch, which is worth doing when only the verdict is wanted.
|
|
1023
|
+
*
|
|
1024
|
+
* @defaultValue true
|
|
1025
|
+
*/
|
|
1026
|
+
suggestions?: boolean;
|
|
1027
|
+
};
|
|
1028
|
+
/**
|
|
1029
|
+
* Enumerate placeable pitches in a range, chord tones first, descending.
|
|
1030
|
+
*
|
|
1031
|
+
* @param q The safety context minus the candidate pitch.
|
|
1032
|
+
* @param pitchLow Lowest MIDI pitch to consider (inclusive).
|
|
1033
|
+
* @param pitchHigh Highest MIDI pitch to consider (inclusive).
|
|
1034
|
+
* @returns Placeable pitches (non-dissonant), chord tones before others, each group descending.
|
|
1035
|
+
* @throws If either bound is not a MIDI pitch in 0..127, reversed, or exceeds
|
|
1036
|
+
* the generation budget, or if the context names an unknown profile.
|
|
1037
|
+
* @example
|
|
1038
|
+
* ```ts
|
|
1039
|
+
* import { enumerateSafePitches, makeChord, majorKey } from '@libraz/libcantus';
|
|
1040
|
+
* const pitches = enumerateSafePitches(
|
|
1041
|
+
* { profile: 'pop', chord: makeChord(0, 'maj'), key: majorKey(0), otherVoices: [], strongBeat: true },
|
|
1042
|
+
* 60,
|
|
1043
|
+
* 72,
|
|
1044
|
+
* );
|
|
1045
|
+
* pitches; // placeable pitches in [60, 72], chord tones first, each group descending
|
|
1046
|
+
* ```
|
|
1047
|
+
* @category Arrangement & Analysis
|
|
1048
|
+
*/
|
|
1049
|
+
declare function enumerateSafePitches(q: Omit<SafetyQuery, 'candidatePitch'>, pitchLow: number, pitchHigh: number): number[];
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Lead-sheet chord symbol parsing and formatting.
|
|
1053
|
+
*
|
|
1054
|
+
* Bridges the informal `<root><quality>[/<bass>]` notation musicians write
|
|
1055
|
+
* (`Cmaj7`, `F#m7b5`, `Bb7`, `C/G`) and the library's structured {@link Chord}
|
|
1056
|
+
* type, so callers can accept/emit plain text at the edges of the API.
|
|
1057
|
+
*
|
|
1058
|
+
* The root and the slash bass are note names, so they are read and written in
|
|
1059
|
+
* whichever notation system the caller names — a German chart's `H7`, `As7` and
|
|
1060
|
+
* `Ges/B` included. Unlike a note name, a chord symbol is never attributed to a
|
|
1061
|
+
* system on its own: German `B` is a B flat and English `B` a B natural, and a
|
|
1062
|
+
* symbol carries no `dur`/`moll` word to break the tie, so the most common
|
|
1063
|
+
* symbol in the vocabulary would otherwise change chord with the source it came
|
|
1064
|
+
* from. With no system given a symbol is English.
|
|
1065
|
+
*/
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Parse a lead-sheet chord symbol into a {@link Chord}.
|
|
1069
|
+
*
|
|
1070
|
+
* Accepts `<root><quality>[/<bass>]`, e.g. `Cmaj7`, `F#m7b5`, `Bb7`, `C/G`,
|
|
1071
|
+
* `C6/9`, `C6/9/E`. Root and bass letters are case-insensitive, matching
|
|
1072
|
+
* {@link parseNote}. A `/` is ambiguous between a slash bass and the `6/9`
|
|
1073
|
+
* quality; the text splits at the last `/` whose right-hand side is a valid
|
|
1074
|
+
* bare note preceded by a recognized quality, so `6/9` (with or without a
|
|
1075
|
+
* further slash bass) parses as a quality.
|
|
1076
|
+
*
|
|
1077
|
+
* The root and bass are read in `system`, so a German chart's `H7`, `As7` and
|
|
1078
|
+
* `Ges/B` are read by asking for `'german'`. The system is never inferred: with
|
|
1079
|
+
* none given the symbol is English, where `B` is the B natural and `H` no note
|
|
1080
|
+
* at all. Case carries nothing in a chord symbol — unlike a German key name,
|
|
1081
|
+
* where the case of the tonic is the mode — so the quality suffix alone says
|
|
1082
|
+
* major or minor. Where a German root's flat suffix could equally open the
|
|
1083
|
+
* quality, the longest root that leaves a known quality behind wins: `Asus4` is
|
|
1084
|
+
* an A suspended chord and `Assus4` the A flat one.
|
|
1085
|
+
*
|
|
1086
|
+
* The quality suffix is read structurally when no name covers it, so a chart's
|
|
1087
|
+
* bracketed tensions parse as readily as the names do: `Cmaj7(#11)`,
|
|
1088
|
+
* `C7(b9,#11)`, `C7(13)`, `Csus4(add9)`, `C-Δ9`, `C6/9(#11)`. A `/` between two
|
|
1089
|
+
* figures separates them as it does inside brackets, which is what the sixth
|
|
1090
|
+
* and ninth of a `6/9` are. Such a chord carries the tones it names and reports
|
|
1091
|
+
* the nearest quality name (see {@link chordSpecQuality}); read
|
|
1092
|
+
* {@link chordSpecOf} for its structure.
|
|
1093
|
+
*
|
|
1094
|
+
* The parsed chord carries the root/bass spellings as enharmonic hints
|
|
1095
|
+
* (`rootSpelling`/`bassSpelling`) so {@link formatChordSymbol} can reproduce
|
|
1096
|
+
* flat spellings such as `Bbmaj7` instead of respelling them with sharps.
|
|
1097
|
+
*
|
|
1098
|
+
* @param text The chord symbol text.
|
|
1099
|
+
* @param opts `system` reads the root and bass in that notation system instead
|
|
1100
|
+
* of English.
|
|
1101
|
+
* @returns The parsed chord.
|
|
1102
|
+
* @throws If the root or quality is not recognized in that system. Use
|
|
1103
|
+
* {@link tryParseChordSymbol} where failure is ordinary, such as a chord
|
|
1104
|
+
* field read on every keystroke.
|
|
1105
|
+
* @example
|
|
1106
|
+
* ```ts
|
|
1107
|
+
* import { parseChordSymbol, formatChordSymbol } from '@libraz/libcantus';
|
|
1108
|
+
* const c = parseChordSymbol('Bbmaj7');
|
|
1109
|
+
* formatChordSymbol(c); // 'Bbmaj7' — flat spelling is preserved on round-trip
|
|
1110
|
+
* parseChordSymbol('H7', { system: 'german' }).rootPc; // 11 — the B natural
|
|
1111
|
+
* parseChordSymbol('B', { system: 'german' }).rootPc; // 10 — the German B flat
|
|
1112
|
+
* ```
|
|
1113
|
+
* @category Chords
|
|
1114
|
+
*/
|
|
1115
|
+
declare function parseChordSymbol(text: string, opts?: NoteNameOptions): Chord;
|
|
1116
|
+
/**
|
|
1117
|
+
* Parse a lead-sheet chord symbol, reporting failure instead of throwing it.
|
|
1118
|
+
*
|
|
1119
|
+
* The same reading as {@link parseChordSymbol} — that function is this one with
|
|
1120
|
+
* its error thrown — for the callers where a symbol that does not parse yet is
|
|
1121
|
+
* the normal state of the input rather than a fault: a chord field says what is
|
|
1122
|
+
* wrong with what has been typed so far, and does not need a `try` around every
|
|
1123
|
+
* keystroke to do it.
|
|
1124
|
+
*
|
|
1125
|
+
* @param text The chord symbol text.
|
|
1126
|
+
* @param opts `system` reads the root and bass in that notation system instead
|
|
1127
|
+
* of English.
|
|
1128
|
+
* @returns The chord, or the error explaining why the text is not one.
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```ts
|
|
1131
|
+
* import { tryParseChordSymbol } from '@libraz/libcantus';
|
|
1132
|
+
* const result = tryParseChordSymbol('Cmaj7(#11)');
|
|
1133
|
+
* result.ok ? result.value.intervals : result.error.message;
|
|
1134
|
+
* ```
|
|
1135
|
+
* @category Chords
|
|
1136
|
+
*/
|
|
1137
|
+
declare function tryParseChordSymbol(text: string, opts?: NoteNameOptions): ParseResult<Chord>;
|
|
1138
|
+
/**
|
|
1139
|
+
* Anything that names a chord: a chord symbol, plain chord data, or a value
|
|
1140
|
+
* that serializes to chord data such as the `Chord` class.
|
|
1141
|
+
*
|
|
1142
|
+
* @category Chords
|
|
1143
|
+
*/
|
|
1144
|
+
type ChordLike = string | Chord | {
|
|
1145
|
+
/** The chord data this value stands for. */
|
|
1146
|
+
toJSON(): Chord;
|
|
1147
|
+
};
|
|
1148
|
+
/**
|
|
1149
|
+
* Resolve any chord-shaped value to plain {@link Chord} data.
|
|
1150
|
+
*
|
|
1151
|
+
* The counterpart of {@link toSpelledInterval} for chords: an entry point takes
|
|
1152
|
+
* whatever form the caller has — the symbol a chart holds, the data the chord
|
|
1153
|
+
* module returns, or a `Chord` instance — and gets one shape back. An instance
|
|
1154
|
+
* is accepted through its `toJSON` method rather than by its type, so the
|
|
1155
|
+
* layers below the model can read a class without importing it.
|
|
1156
|
+
*
|
|
1157
|
+
* @param value A chord symbol, plain chord data, or a value whose `toJSON`
|
|
1158
|
+
* returns chord data.
|
|
1159
|
+
* @returns The validated chord data, with root and bass reduced to pitch
|
|
1160
|
+
* classes and any spelling hint carried through.
|
|
1161
|
+
* @throws If the value names no chord, or an interval, root, or bass is not a
|
|
1162
|
+
* finite number.
|
|
1163
|
+
* @example
|
|
1164
|
+
* ```ts
|
|
1165
|
+
* import { toChordData } from '@libraz/libcantus';
|
|
1166
|
+
* toChordData('Cmaj7').intervals; // [0, 4, 7, 11]
|
|
1167
|
+
* toChordData({ rootPc: 0, quality: 'maj', intervals: [0, 4, 7] }).rootPc; // 0
|
|
1168
|
+
* ```
|
|
1169
|
+
* @category Chords
|
|
1170
|
+
*/
|
|
1171
|
+
declare function toChordData(value: ChordLike): Chord;
|
|
1172
|
+
/**
|
|
1173
|
+
* How a chord symbol is written: in which notation system, and on which side of
|
|
1174
|
+
* the enharmonic fence.
|
|
1175
|
+
*
|
|
1176
|
+
* @category Chords
|
|
1177
|
+
*/
|
|
1178
|
+
type ChordSymbolOptions = NoteNameOptions & {
|
|
1179
|
+
/**
|
|
1180
|
+
* Prefer flat spellings over sharps for an altered root or bass; passing
|
|
1181
|
+
* either `true` or `false` overrides any spelling hint the chord carries.
|
|
1182
|
+
*/
|
|
1183
|
+
flats?: boolean;
|
|
1184
|
+
};
|
|
1185
|
+
/**
|
|
1186
|
+
* Format a {@link Chord} as a lead-sheet chord symbol.
|
|
1187
|
+
*
|
|
1188
|
+
* The inverse of {@link parseChordSymbol} in every notation system: each
|
|
1189
|
+
* quality maps to one canonical suffix spelling, the root and bass are written
|
|
1190
|
+
* in `system`, and a slash bass is appended only when it differs from the root.
|
|
1191
|
+
* Every symbol written here reads back through {@link parseChordSymbol} as the
|
|
1192
|
+
* same root, sounding bass and tones — whatever combination of tensions the
|
|
1193
|
+
* chord carries, and whether or not a quality name covers it.
|
|
1194
|
+
* When the chord carries `rootSpelling`/`bassSpelling` hints (as chords from
|
|
1195
|
+
* {@link parseChordSymbol} do) and no explicit `flats` preference is given, the
|
|
1196
|
+
* hints are reused so flat symbols round-trip unchanged.
|
|
1197
|
+
*
|
|
1198
|
+
* The suffix is written from the chord's own tones (see {@link chordSpecOf}),
|
|
1199
|
+
* not from its quality name, so a chord carrying alterations no name covers is
|
|
1200
|
+
* written out in full — `C7(b9,#11)` — rather than under the nearest name. A
|
|
1201
|
+
* tone the chord leaves out is not written: the symbol names the harmony, so a
|
|
1202
|
+
* fifth-less dominant is still `C7`.
|
|
1203
|
+
*
|
|
1204
|
+
* @param chord The chord to format, as a chord symbol, chord data, or a
|
|
1205
|
+
* `Chord`.
|
|
1206
|
+
* @param opts `system` writes the root and bass in that notation system instead
|
|
1207
|
+
* of English; `flats` overrides the spelling hints, as
|
|
1208
|
+
* {@link ChordSymbolOptions} describes.
|
|
1209
|
+
* @returns The chord symbol text.
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```ts
|
|
1212
|
+
* import { makeChord, formatChordSymbol, parseChordSymbol } from '@libraz/libcantus';
|
|
1213
|
+
* formatChordSymbol(makeChord(0, 'min7')); // 'Cm7'
|
|
1214
|
+
* formatChordSymbol(parseChordSymbol('Bb7'), { system: 'german' }); // 'B7'
|
|
1215
|
+
* formatChordSymbol(parseChordSymbol('B7'), { system: 'german' }); // 'H7'
|
|
1216
|
+
* ```
|
|
1217
|
+
* @category Chords
|
|
1218
|
+
*/
|
|
1219
|
+
declare function formatChordSymbol(chord: ChordLike, opts?: ChordSymbolOptions): string;
|
|
1220
|
+
/**
|
|
1221
|
+
* Transpose a chord symbol by a number of semitones.
|
|
1222
|
+
*
|
|
1223
|
+
* The symbol is read in the same system it is written back in, so a German
|
|
1224
|
+
* chart stays German across the transposition.
|
|
1225
|
+
*
|
|
1226
|
+
* @param text The chord symbol text.
|
|
1227
|
+
* @param semitones Signed semitone offset to apply to the root and bass.
|
|
1228
|
+
* @param opts Options for reading and writing the symbol; see
|
|
1229
|
+
* {@link ChordSymbolOptions}.
|
|
1230
|
+
* @returns The transposed chord symbol text.
|
|
1231
|
+
* @throws If `text` does not parse as a chord symbol.
|
|
1232
|
+
* @example
|
|
1233
|
+
* ```ts
|
|
1234
|
+
* import { transposeChordSymbol } from '@libraz/libcantus';
|
|
1235
|
+
* transposeChordSymbol('C/G', 2); // 'D/A'
|
|
1236
|
+
* transposeChordSymbol('H7', 1, { system: 'german' }); // 'C7'
|
|
1237
|
+
* ```
|
|
1238
|
+
* @category Chords
|
|
1239
|
+
*/
|
|
1240
|
+
declare function transposeChordSymbol(text: string, semitones: number, opts?: ChordSymbolOptions): string;
|
|
1241
|
+
|
|
1242
|
+
export { toChordData as $, type Alteration as A, diatonicTriad as B, type ChordBase as C, dominantKeyOf as D, type EvaluateSafetyOptions as E, enharmonicKeyOf as F, enumerateSafePitches as G, evaluateSafety as H, formatChordSymbol as I, intervalAboveRoot as J, type KeyLike as K, isChordMember as L, keyRelationBetween as M, NoteSafety as N, makeChord as O, PROFILE_WEIGHTS as P, parallelKeyOf as Q, ReasonFlag as R, type SafetyProfile as S, parseChordSymbol as T, profileWeights as U, type VoiceSnapshot as V, relatedKeysOf as W, relativeKeyOf as X, spanFromChord as Y, spelledKeyOf as Z, subdominantKeyOf as _, type AlteredDegree as a, toKeyScale as a0, transposeChord as a1, transposeChordSymbol as a2, tryParseChordSymbol as a3, type Chord as b, type ChordLike as c, type ChordQuality as d, type ChordSegment as e, type ChordSeventh as f, type ChordSpan as g, type ChordSpec as h, type ChordSymbolOptions as i, type ChordToneRole as j, type KeyRelation as k, type PitchSpelling as l, type ProfileWeights as m, type SafetyQuery as n, type SafetyResult as o, type SpelledKey as p, chordFromDegree as q, chordFromSpan as r, chordFromSpec as s, chordPitchClasses as t, chordQualities as u, chordSpecIntervals as v, chordSpecOf as w, chordSpecQuality as x, chordToneRole as y, diatonicSeventh as z };
|