@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,1095 @@
|
|
|
1
|
+
import { f as Note, d as KeyScale, h as NoteLike, I as IntervalLike, S as SpelledInterval } from './index-B9MNZboQ.cjs';
|
|
2
|
+
import { r as ScaleName, u as WorldScaleName, s as ScaleNameInput } from './masks-DYbE485J.cjs';
|
|
3
|
+
import { c as ChordLike, b as Chord, K as KeyLike } from './index-C_8o--VE.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Which of the two modes a key signature is read in.
|
|
7
|
+
*
|
|
8
|
+
* A signature names two keys — three sharps is both A major and F# minor — so
|
|
9
|
+
* every entry point that turns a signature into a key, or that names the mode
|
|
10
|
+
* of a key relation, selects between them with this.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import type { KeyMode } from '@libraz/libcantus';
|
|
15
|
+
* const mode: KeyMode = 'minor';
|
|
16
|
+
* ```
|
|
17
|
+
* @category Scales
|
|
18
|
+
*/
|
|
19
|
+
type KeyMode = 'major' | 'minor';
|
|
20
|
+
/**
|
|
21
|
+
* The key signature of a key, as a signed count of sharps (positive) or flats
|
|
22
|
+
* (negative) — the `fifths` value a notation format such as MusicXML writes.
|
|
23
|
+
*
|
|
24
|
+
* The tonic's letter and accidental place the key on the circle of fifths, and
|
|
25
|
+
* the mode moves it from there: lydian +1, major 0, mixolydian -1, dorian -2,
|
|
26
|
+
* natural minor -3, phrygian -4, locrian -5.
|
|
27
|
+
*
|
|
28
|
+
* A scale that is not a diatonic mode has no signature of its own, so it takes
|
|
29
|
+
* the signature of its parallel major or minor: a scale with a minor third and
|
|
30
|
+
* no major third counts as minor (-3), everything else as major (0). This is
|
|
31
|
+
* why A harmonic minor reports 0 — its G# is written as an accidental, not in
|
|
32
|
+
* the signature — and why A melodic minor reports 0 as well.
|
|
33
|
+
*
|
|
34
|
+
* The result is not clamped: a theoretical key such as Fb major reports -8.
|
|
35
|
+
* Clamp to ±7 if the consuming format cannot express more.
|
|
36
|
+
*
|
|
37
|
+
* @param tonic The spelled tonic, as a note name, note data, or a `Note`; its
|
|
38
|
+
* accidental is worth seven fifths.
|
|
39
|
+
* @param key The key/scale. This one stays a plain key/scale: it reads any
|
|
40
|
+
* 12-bit mask, including the tonic-less ones a key name cannot describe.
|
|
41
|
+
* @returns The signed number of sharps (positive) or flats (negative).
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { keySignatureFifths, majorKey, minorKey, parseNote } from '@libraz/libcantus';
|
|
45
|
+
* keySignatureFifths(parseNote('Bb'), majorKey(10)); // -2
|
|
46
|
+
* keySignatureFifths('A', minorKey(9)); // 0
|
|
47
|
+
* ```
|
|
48
|
+
* @category Scales
|
|
49
|
+
*/
|
|
50
|
+
declare function keySignatureFifths(tonic: NoteLike, key: KeyScale): number;
|
|
51
|
+
/**
|
|
52
|
+
* The key a signature denotes: the inverse of {@link keySignatureFifths} for
|
|
53
|
+
* the two modes a signature is conventionally read in.
|
|
54
|
+
*
|
|
55
|
+
* A signature names two keys — three sharps is both A major and F# minor — so
|
|
56
|
+
* the mode decides which one is built. The tonic is spelled the way that key is
|
|
57
|
+
* written, including the theoretical keys past ±7 (nine sharps is B# major).
|
|
58
|
+
*
|
|
59
|
+
* @param fifths The signed number of sharps (positive) or flats (negative).
|
|
60
|
+
* @param mode Which of the signature's two keys to build; defaults to major.
|
|
61
|
+
* @returns The spelled tonic and the matching key/scale.
|
|
62
|
+
* @throws If `fifths` is not an integer in [-12, 12], or `mode` is not one of
|
|
63
|
+
* `'major'` or `'minor'`.
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { keyFromFifths, formatNote } from '@libraz/libcantus';
|
|
67
|
+
* formatNote(keyFromFifths(-2).tonic); // 'Bb'
|
|
68
|
+
* formatNote(keyFromFifths(-2, 'minor').tonic); // 'G'
|
|
69
|
+
* ```
|
|
70
|
+
* @category Scales
|
|
71
|
+
*/
|
|
72
|
+
declare function keyFromFifths(fifths: number, mode?: KeyMode): {
|
|
73
|
+
tonic: Note;
|
|
74
|
+
key: KeyScale;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Scale systems: which pitch organisations functional harmony was built for,
|
|
79
|
+
* and which ones it would only be imposed on.
|
|
80
|
+
*
|
|
81
|
+
* Roman numerals, tonic/subdominant/dominant function and cadence detection all
|
|
82
|
+
* presuppose Western common practice. A `KeyScale` is a root pitch class and a
|
|
83
|
+
* twelve-bit mask, which says nothing about the tradition the scale came from,
|
|
84
|
+
* so analysis code that wants to branch instead of assuming needs this
|
|
85
|
+
* classification to branch on.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The kind of pitch organisation a scale belongs to.
|
|
90
|
+
*
|
|
91
|
+
* - `'common-practice'`: the scale functional harmony is defined on — a tonic,
|
|
92
|
+
* a dominant carrying a leading tone, and a triad available on every degree.
|
|
93
|
+
* - `'modal'`: a seven-note rotation or alteration of that material. Degrees
|
|
94
|
+
* and Roman numerals still describe it, but a dominant-to-tonic cadence is
|
|
95
|
+
* not what holds the music together.
|
|
96
|
+
* - `'non-functional'`: the collection carries no chord function of its own. A
|
|
97
|
+
* gapped or symmetric set takes its harmony from whatever key it is used
|
|
98
|
+
* over, and a scale from a tradition organised melodically rather than by
|
|
99
|
+
* chords has no functional reading at all.
|
|
100
|
+
*
|
|
101
|
+
* @category Scales
|
|
102
|
+
*/
|
|
103
|
+
type ScaleSystem = 'common-practice' | 'modal' | 'non-functional';
|
|
104
|
+
/**
|
|
105
|
+
* The system each built-in scale belongs to.
|
|
106
|
+
*
|
|
107
|
+
* Every name in {@link NAMED_SCALES} and {@link WORLD_SCALES} appears here, so
|
|
108
|
+
* a scale added without a classification fails to compile rather than falling
|
|
109
|
+
* silently into a default. Aliases are classified through the name they resolve
|
|
110
|
+
* to; use {@link scaleSystemOf} to look one up.
|
|
111
|
+
*
|
|
112
|
+
* @category Scales
|
|
113
|
+
*/
|
|
114
|
+
declare const SCALE_SYSTEMS: Readonly<Record<ScaleName | WorldScaleName, ScaleSystem>>;
|
|
115
|
+
/**
|
|
116
|
+
* The system a scale belongs to.
|
|
117
|
+
*
|
|
118
|
+
* A name — canonical or an alias — is answered exactly: `'nahawand'` is a maqam
|
|
119
|
+
* even though its mask is the natural minor's. A `KeyScale` carries no
|
|
120
|
+
* tradition, so it is answered by its mask, under the Western reading of that
|
|
121
|
+
* mask; a mask that is no built-in scale answers undefined.
|
|
122
|
+
*
|
|
123
|
+
* @param scale The scale name, or a key/scale to read the mask of.
|
|
124
|
+
* @returns The system, or undefined for a mask that names no built-in scale.
|
|
125
|
+
* @throws If `scale` is a string naming no built-in scale. An unknown name is a
|
|
126
|
+
* typo, and answering it the way an unrecognised mask is answered would hide
|
|
127
|
+
* that.
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { scaleSystemOf, majorKey } from '@libraz/libcantus';
|
|
131
|
+
* scaleSystemOf('harmonicMinor'); // 'common-practice'
|
|
132
|
+
* scaleSystemOf('dorian'); // 'modal'
|
|
133
|
+
* scaleSystemOf('hijaz'); // 'non-functional'
|
|
134
|
+
* scaleSystemOf(majorKey(0)); // 'common-practice'
|
|
135
|
+
* ```
|
|
136
|
+
* @category Scales
|
|
137
|
+
*/
|
|
138
|
+
declare function scaleSystemOf(scale: KeyScale | ScaleNameInput): ScaleSystem | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* Whether functional (Roman-numeral) analysis applies to a scale.
|
|
141
|
+
*
|
|
142
|
+
* True for common-practice and modal scales, false for everything classed
|
|
143
|
+
* `'non-functional'` — analysis that would otherwise read a raga as a chord
|
|
144
|
+
* progression can ask here first.
|
|
145
|
+
*
|
|
146
|
+
* The two ways of naming a scale are answered differently, exactly as
|
|
147
|
+
* {@link scaleSystemOf} answers them. A name carries its tradition, so
|
|
148
|
+
* `'hijaz'` is false. A `KeyScale` carries only a mask, and a mask shared with
|
|
149
|
+
* a Western scale is read the Western way — maqam Hijaz and the phrygian
|
|
150
|
+
* dominant are the same seven pitch classes, so that mask is modal and answers
|
|
151
|
+
* true. A mask that names no built-in scale answers true as well, which keeps a
|
|
152
|
+
* caller's own mask being analysed the way it always has been. Pass the name
|
|
153
|
+
* whenever the tradition is known and the answer should follow it.
|
|
154
|
+
*
|
|
155
|
+
* @param scale The scale name, or a key/scale to read the mask of.
|
|
156
|
+
* @returns True when functional harmony describes the scale.
|
|
157
|
+
* @throws If `scale` is a string naming no built-in scale.
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* import { supportsFunctionalHarmony, scaleByName } from '@libraz/libcantus';
|
|
161
|
+
* supportsFunctionalHarmony('harmonicMinor'); // true
|
|
162
|
+
* supportsFunctionalHarmony('bhairav'); // false
|
|
163
|
+
* supportsFunctionalHarmony('hijaz'); // false — named, so read as a maqam
|
|
164
|
+
* supportsFunctionalHarmony(scaleByName('hijaz', 0)); // true — its mask is the phrygian dominant
|
|
165
|
+
* supportsFunctionalHarmony(scaleByName('ryukyu', 0)); // false — a mask no Western scale shares
|
|
166
|
+
* ```
|
|
167
|
+
* @category Scales
|
|
168
|
+
*/
|
|
169
|
+
declare function supportsFunctionalHarmony(scale: KeyScale | ScaleNameInput): boolean;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Test whether every chord pitch class is contained in a scale.
|
|
173
|
+
*
|
|
174
|
+
* @param chordPcs The chord's pitch classes (0..11).
|
|
175
|
+
* @param scaleMask The scale's 12-bit mode mask.
|
|
176
|
+
* @param scaleRootPc The pitch class the mask is rooted on.
|
|
177
|
+
* @returns True if the scale is a superset of the chord.
|
|
178
|
+
*
|
|
179
|
+
* @category Scales
|
|
180
|
+
*/
|
|
181
|
+
declare function scaleMatchesChord(chordPcs: number[], scaleMask: number, scaleRootPc: number): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* A named scale rooted on a pitch class that fits over a chord.
|
|
184
|
+
*
|
|
185
|
+
* @category Scales
|
|
186
|
+
*/
|
|
187
|
+
type ChordScaleMatch = {
|
|
188
|
+
name: string;
|
|
189
|
+
rootPc: number;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* List the scales that fit over a chord, best fit first.
|
|
193
|
+
*
|
|
194
|
+
* Only the chord root is considered as the scale root, matching the
|
|
195
|
+
* conventional chord-scale relationship. Every entry of {@link NAMED_SCALES}
|
|
196
|
+
* whose pitch-class set is a superset of the chord's is returned once per
|
|
197
|
+
* distinct pitch-class set (aliased masks such as major/ionian report only the
|
|
198
|
+
* modal name), ranked by fewest extra scale tones beyond the chord and then by
|
|
199
|
+
* conventional
|
|
200
|
+
* preference. For bare triads and smaller chords, heptatonic scales rank before
|
|
201
|
+
* pentatonics and other sizes: a pentatonic adds no modal color over a triad,
|
|
202
|
+
* so the seven-note modes are the more useful answer. The chromatic scale is
|
|
203
|
+
* only returned as a fallback when no other scale contains the chord.
|
|
204
|
+
*
|
|
205
|
+
* The ranking is by fit, so the first entry is the tightest-fitting scale
|
|
206
|
+
* rather than the idiomatic choice: a four-note chord that a pentatonic covers
|
|
207
|
+
* exactly puts that pentatonic ahead of the mode a player would name. Read the
|
|
208
|
+
* whole list, or pick by name, when the idiomatic scale is what is wanted;
|
|
209
|
+
* {@link scalesForChanges} makes that choice for a progression.
|
|
210
|
+
*
|
|
211
|
+
* @param chord The chord to fit scales over, as a chord symbol, chord data, or
|
|
212
|
+
* a `Chord`.
|
|
213
|
+
* @returns The matching scales rooted on the chord root, best fit first.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* import { makeChord, chordScales } from '@libraz/libcantus';
|
|
218
|
+
* const scales = chordScales(makeChord(0, 'maj7'));
|
|
219
|
+
* // scales for Cmaj7, best fit first; each is { name, rootPc: 0 }
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @category Scales
|
|
223
|
+
*/
|
|
224
|
+
declare function chordScales(chord: ChordLike): ChordScaleMatch[];
|
|
225
|
+
/**
|
|
226
|
+
* Which use an avoid note is judged for: sounding it, or passing through it.
|
|
227
|
+
*
|
|
228
|
+
* `'harmonic'` is the Berklee-style rule in full — a tone that must not be
|
|
229
|
+
* sounded against the chord. `'melodic'` keeps only what a line cannot pass
|
|
230
|
+
* through either, which is the semitone above the root; every other clash
|
|
231
|
+
* resolves as the line moves on.
|
|
232
|
+
*
|
|
233
|
+
* @category Scales
|
|
234
|
+
*/
|
|
235
|
+
type AvoidNoteUse = 'harmonic' | 'melodic';
|
|
236
|
+
/**
|
|
237
|
+
* Options for {@link avoidNotes}.
|
|
238
|
+
*
|
|
239
|
+
* @category Scales
|
|
240
|
+
*/
|
|
241
|
+
type AvoidNotesOptions = {
|
|
242
|
+
use?: AvoidNoteUse;
|
|
243
|
+
};
|
|
244
|
+
/**
|
|
245
|
+
* List a scale's avoid notes over a chord.
|
|
246
|
+
*
|
|
247
|
+
* An avoid note is a non-chord scale tone that lies a semitone directly above a
|
|
248
|
+
* chord tone, or the third a suspension displaced; sounding it against the
|
|
249
|
+
* chord clashes. The scale is rooted on the chord root. If the scale does not
|
|
250
|
+
* contain the chord, no avoid notes exist.
|
|
251
|
+
*
|
|
252
|
+
* The rule states which tones must not be *sounded* against the chord, which is
|
|
253
|
+
* not the same question as which tones a line may not touch. Pass
|
|
254
|
+
* `{ use: 'melodic' }` for the melodic reading: only the semitone above the
|
|
255
|
+
* root survives it, so the result is always a subset of the harmonic one.
|
|
256
|
+
*
|
|
257
|
+
* @param chord The chord providing the chord tones, as a chord symbol, chord
|
|
258
|
+
* data, or a `Chord`.
|
|
259
|
+
* @param scaleName A key of {@link NAMED_SCALES}.
|
|
260
|
+
* @param opts Set `use: 'melodic'` to judge a line rather than a voicing.
|
|
261
|
+
* @returns The avoid-note pitch classes, sorted ascending in [0, 11].
|
|
262
|
+
* @throws If `scaleName` is not a built-in scale. An empty result already
|
|
263
|
+
* means "this scale has no avoid notes over this chord"; answering a typo
|
|
264
|
+
* the same way would make the two indistinguishable.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* import { makeChord, avoidNotes } from '@libraz/libcantus';
|
|
269
|
+
* avoidNotes(makeChord(0, 'maj7'), 'ionian'); // [5] — F clashes with the third
|
|
270
|
+
* avoidNotes(makeChord(0, 'maj7'), 'ionian', { use: 'melodic' }); // [] — a line may pass through it
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* @category Scales
|
|
274
|
+
*/
|
|
275
|
+
declare function avoidNotes(chord: ChordLike, scaleName: ScaleNameInput, opts?: AvoidNotesOptions): number[];
|
|
276
|
+
/**
|
|
277
|
+
* Options for {@link availableTensions}.
|
|
278
|
+
*
|
|
279
|
+
* @category Scales
|
|
280
|
+
*/
|
|
281
|
+
type AvailableTensionsOptions = {
|
|
282
|
+
/**
|
|
283
|
+
* The chord this one resolves to, as a chord symbol, chord data, or a
|
|
284
|
+
* `Chord`.
|
|
285
|
+
*/
|
|
286
|
+
resolvesTo?: ChordLike;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* List a scale's available tensions over a chord.
|
|
290
|
+
*
|
|
291
|
+
* Available tensions are scale tones that are neither chord tones nor avoid
|
|
292
|
+
* notes, i.e. the usable color tones (typically the 9/11/13 region). The scale
|
|
293
|
+
* is rooted on the chord root. If the scale does not contain the chord, there
|
|
294
|
+
* are no available tensions.
|
|
295
|
+
*
|
|
296
|
+
* A tension's availability also depends on what the chord is doing. Pass
|
|
297
|
+
* `resolvesTo` to say which chord this one resolves to: a dominant seventh
|
|
298
|
+
* resolving down a fifth to a minor tonic takes its flat ninth, sharp ninth and
|
|
299
|
+
* flat thirteenth, which read on their own would each be an avoid note. Only
|
|
300
|
+
* scale tones are ever added, so the result stays within the scale.
|
|
301
|
+
*
|
|
302
|
+
* @param chord The chord providing the chord tones, as a chord symbol, chord
|
|
303
|
+
* data, or a `Chord`.
|
|
304
|
+
* @param scaleName A key of {@link NAMED_SCALES}.
|
|
305
|
+
* @param opts Set `resolvesTo` to the chord this one resolves to.
|
|
306
|
+
* @returns The available-tension pitch classes, sorted ascending in [0, 11].
|
|
307
|
+
* @throws If `scaleName` is not a built-in scale, as {@link avoidNotes} does.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* import { makeChord, availableTensions } from '@libraz/libcantus';
|
|
312
|
+
* availableTensions(makeChord(0, 'maj7'), 'ionian'); // [2, 9] — color tones over Cmaj7
|
|
313
|
+
* availableTensions(makeChord(7, 'dom7'), 'phrygianDominant', { resolvesTo: makeChord(0, 'min') });
|
|
314
|
+
* // [3, 8] — the b13 and b9 the resolution to C minor makes available
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* @category Scales
|
|
318
|
+
*/
|
|
319
|
+
declare function availableTensions(chord: ChordLike, scaleName: ScaleNameInput, opts?: AvailableTensionsOptions): number[];
|
|
320
|
+
/**
|
|
321
|
+
* A scale fit over a chord, with its non-chord tones sorted by what may be done
|
|
322
|
+
* with them.
|
|
323
|
+
*
|
|
324
|
+
* `avoid`, `passing` and `tensions` partition the scale tones the chord does
|
|
325
|
+
* not state: `avoid` may not be played at all, `passing` may be passed through
|
|
326
|
+
* melodically but not sounded against the chord, and `tensions` may be added
|
|
327
|
+
* freely as color. `avoid` is {@link avoidNotes} under `use: 'melodic'`, and
|
|
328
|
+
* `avoid` together with `passing` is the harmonic reading.
|
|
329
|
+
*
|
|
330
|
+
* @category Scales
|
|
331
|
+
*/
|
|
332
|
+
type ChordScaleReportEntry = {
|
|
333
|
+
name: string;
|
|
334
|
+
rootPc: number;
|
|
335
|
+
avoid: number[];
|
|
336
|
+
passing: number[];
|
|
337
|
+
tensions: number[];
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* Report the best-fitting scales for a chord with their avoid notes and tensions.
|
|
341
|
+
*
|
|
342
|
+
* Combines {@link chordScales}, {@link avoidNotes}, and {@link availableTensions}
|
|
343
|
+
* into a single ergonomic result, ordered best fit first. The report is read as
|
|
344
|
+
* playing advice, so the tones a line may pass through are reported apart from
|
|
345
|
+
* the ones it may not touch; see {@link ChordScaleReportEntry}.
|
|
346
|
+
*
|
|
347
|
+
* @param chord The chord to analyze, as a chord symbol, chord data, or a
|
|
348
|
+
* `Chord`.
|
|
349
|
+
* @param limit Optional maximum number of scales to report; all by default.
|
|
350
|
+
* @returns One entry per reported scale, best fit first.
|
|
351
|
+
*
|
|
352
|
+
* @category Scales
|
|
353
|
+
*/
|
|
354
|
+
declare function chordScaleReport(chord: ChordLike, limit?: number): ChordScaleReportEntry[];
|
|
355
|
+
/**
|
|
356
|
+
* A chord paired with the scale chosen for it by {@link scalesForChanges}.
|
|
357
|
+
*
|
|
358
|
+
* @category Scales
|
|
359
|
+
*/
|
|
360
|
+
type ScaleChoice = {
|
|
361
|
+
chord: Chord;
|
|
362
|
+
scale: ChordScaleMatch;
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* Choose one scale per chord across a progression, favoring continuity.
|
|
366
|
+
*
|
|
367
|
+
* Each chord's {@link chordScales} candidates form a stage in a Viterbi-style
|
|
368
|
+
* dynamic program. The transition cost between adjacent choices is the number
|
|
369
|
+
* of pitch classes that differ between their pitch-class sets (the symmetric
|
|
370
|
+
* difference), plus a small penalty for straying from a chord's best-fit scale
|
|
371
|
+
* so that ties break toward the tighter fit. The minimum-total-cost path is
|
|
372
|
+
* returned, one {@link ScaleChoice} per input chord in the original order.
|
|
373
|
+
*
|
|
374
|
+
* Each choice pairs its scale with the chord exactly as it was handed in, so
|
|
375
|
+
* this takes plain chord data rather than the wider forms the entry points
|
|
376
|
+
* above accept.
|
|
377
|
+
*
|
|
378
|
+
* @param chords The chord sequence to choose scales for.
|
|
379
|
+
* @returns One scale choice per chord, in input order.
|
|
380
|
+
*
|
|
381
|
+
* @category Scales
|
|
382
|
+
*/
|
|
383
|
+
declare function scalesForChanges(chords: readonly Chord[]): ScaleChoice[];
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Voice independence as a measurement rather than a verdict.
|
|
387
|
+
*
|
|
388
|
+
* The species rules answer "is this allowed", which is the wrong question of a
|
|
389
|
+
* pop arrangement: a harmony line in parallel thirds, a power-chord riff and a
|
|
390
|
+
* pedal point all break them and all are the intended sound. This module
|
|
391
|
+
* reports how independent two lines actually are and leaves the judgement to
|
|
392
|
+
* the caller.
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* How two lines move against each other, and how far apart they sit.
|
|
397
|
+
*
|
|
398
|
+
* @category Voicing & Counterpoint
|
|
399
|
+
*/
|
|
400
|
+
type VoiceIndependenceReport = {
|
|
401
|
+
/**
|
|
402
|
+
* Share of the moves that fall into each motion type, summing to 1. A slot
|
|
403
|
+
* pair in which neither voice moves is not a move and is left out entirely,
|
|
404
|
+
* so an accompaniment that mostly sits still can still report mostly contrary
|
|
405
|
+
* motion. All four are 0 when nothing moves.
|
|
406
|
+
*/
|
|
407
|
+
motion: {
|
|
408
|
+
/** The voices move in opposite directions. */
|
|
409
|
+
contrary: number;
|
|
410
|
+
/** One voice holds while the other moves. */
|
|
411
|
+
oblique: number;
|
|
412
|
+
/** Both rise or both fall, and the interval between them changes. */
|
|
413
|
+
similar: number;
|
|
414
|
+
/** Both rise or both fall, keeping the same interval. */
|
|
415
|
+
parallel: number;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Share of the slots where the lead holds or rests on which the counter
|
|
419
|
+
* attacks: 1 when the counter fills every gap the lead leaves, 0 when it moves
|
|
420
|
+
* only where the lead does. It is 0 when the lead never holds or rests, since
|
|
421
|
+
* there was nothing to complement.
|
|
422
|
+
*/
|
|
423
|
+
rhythmicComplementarity: number;
|
|
424
|
+
/** Distance between the two lines, in semitones, over the slots where both sound. */
|
|
425
|
+
separation: {
|
|
426
|
+
/** Mean distance; 0 when the two lines never sound together. */
|
|
427
|
+
mean: number;
|
|
428
|
+
/** Closest approach; 0 when the two lines never sound together. */
|
|
429
|
+
min: number;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Slots at which the two lines sit in the opposite order to the one they keep
|
|
433
|
+
* overall. A unison is not a crossing.
|
|
434
|
+
*/
|
|
435
|
+
crossings: number;
|
|
436
|
+
/** Longest run of consecutive slots sounding a perfect consonance. */
|
|
437
|
+
longestPerfectRun: number;
|
|
438
|
+
/** Slots at which both lines sound, the denominator of `separation`. */
|
|
439
|
+
sounding: number;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Options controlling {@link voiceIndependence}.
|
|
443
|
+
*
|
|
444
|
+
* @category Voicing & Counterpoint
|
|
445
|
+
*/
|
|
446
|
+
type VoiceIndependenceOptions = {
|
|
447
|
+
/**
|
|
448
|
+
* Which slots are fresh attacks in the lead. Without it a slot counts as an
|
|
449
|
+
* attack when its note differs from the previous slot's, which reads a
|
|
450
|
+
* deliberately repeated note as a sustain.
|
|
451
|
+
*/
|
|
452
|
+
leadAttacks?: readonly boolean[];
|
|
453
|
+
/** Which slots are fresh attacks in the counter; read as `leadAttacks` is. */
|
|
454
|
+
counterAttacks?: readonly boolean[];
|
|
455
|
+
/**
|
|
456
|
+
* Count the perfect fourth among the perfect consonances when measuring
|
|
457
|
+
* `longestPerfectRun`. Useful for quartal writing, where the fourth is the
|
|
458
|
+
* interval being sustained.
|
|
459
|
+
*
|
|
460
|
+
* @defaultValue false
|
|
461
|
+
*/
|
|
462
|
+
countFourths?: boolean;
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* Measure how independent a counter line is from the lead it accompanies.
|
|
466
|
+
*
|
|
467
|
+
* Both lines are index-aligned samples: one slot per beat (or whatever grid the
|
|
468
|
+
* caller works on), `null` for a rest, and a repeated note for a sustain. That
|
|
469
|
+
* is what makes the rhythmic reading possible without a duration model, and
|
|
470
|
+
* what makes a re-attacked repeated note look like a sustain unless
|
|
471
|
+
* `leadAttacks` / `counterAttacks` say otherwise. Notes must carry octaves,
|
|
472
|
+
* since separation and motion are measured between sounding pitches.
|
|
473
|
+
*
|
|
474
|
+
* Nothing here is a verdict. Parallel thirds report as parallel motion with a
|
|
475
|
+
* small separation, a pedal point as oblique motion, a power-chord line as a
|
|
476
|
+
* long perfect run — all facts about the texture, not faults in it.
|
|
477
|
+
*
|
|
478
|
+
* @param lead The leading line, one entry per slot.
|
|
479
|
+
* @param counter The accompanying line, aligned slot for slot with `lead`.
|
|
480
|
+
* @param opts Attack flags and the treatment of the fourth.
|
|
481
|
+
* @returns The motion breakdown, rhythmic complementarity, separation,
|
|
482
|
+
* crossings, and the longest perfect-consonance run.
|
|
483
|
+
* @throws If the two lines differ in length, if an attack array is supplied at
|
|
484
|
+
* another length than the lines, or if a note carries no octave.
|
|
485
|
+
* @example
|
|
486
|
+
* ```ts
|
|
487
|
+
* import { parseNote, voiceIndependence } from '@libraz/libcantus';
|
|
488
|
+
* const lead = ['C5', 'D5', 'E5'].map((n) => parseNote(n));
|
|
489
|
+
* const counter = ['E4', 'F4', 'G4'].map((n) => parseNote(n));
|
|
490
|
+
* voiceIndependence(lead, counter).motion.parallel; // 1 — a harmony line in sixths
|
|
491
|
+
* ```
|
|
492
|
+
* @category Voicing & Counterpoint
|
|
493
|
+
*/
|
|
494
|
+
declare function voiceIndependence(lead: readonly (Note | null)[], counter: readonly (Note | null)[], opts?: VoiceIndependenceOptions): VoiceIndependenceReport;
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* An inclusive MIDI pitch range for a single voice.
|
|
498
|
+
*
|
|
499
|
+
* @category Voicing & Counterpoint
|
|
500
|
+
*/
|
|
501
|
+
type VoiceRange = {
|
|
502
|
+
readonly min: number;
|
|
503
|
+
readonly max: number;
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* Default four-voice SATB ranges, ascending (index 0 = lowest):
|
|
507
|
+
* bass E2–C4 (40–60), tenor C3–G4 (48–67), alto G3–D5 (55–74),
|
|
508
|
+
* soprano C4–G5 (60–79).
|
|
509
|
+
*
|
|
510
|
+
* @category Voicing & Counterpoint
|
|
511
|
+
*/
|
|
512
|
+
declare const SATB_RANGES: readonly Readonly<VoiceRange>[];
|
|
513
|
+
/**
|
|
514
|
+
* Options controlling {@link voiceChord} and {@link voiceProgression}.
|
|
515
|
+
*
|
|
516
|
+
* @category Voicing & Counterpoint
|
|
517
|
+
*/
|
|
518
|
+
type VoicingOptions = {
|
|
519
|
+
/**
|
|
520
|
+
* Number of voices to realize. Ignored when `ranges` is given.
|
|
521
|
+
*
|
|
522
|
+
* @defaultValue 4
|
|
523
|
+
*/
|
|
524
|
+
voices?: number;
|
|
525
|
+
/** Explicit per-voice ranges, ascending (index 0 = lowest). Takes precedence over `voices`. */
|
|
526
|
+
ranges?: VoiceRange[];
|
|
527
|
+
/**
|
|
528
|
+
* Maximum spacing in semitones between adjacent upper voices.
|
|
529
|
+
*
|
|
530
|
+
* @defaultValue 12
|
|
531
|
+
*/
|
|
532
|
+
maxSpacing?: number;
|
|
533
|
+
/**
|
|
534
|
+
* Maximum candidate voicings evaluated for one chord.
|
|
535
|
+
*
|
|
536
|
+
* Raise this for large voicings when exact optimum matters more than bounded
|
|
537
|
+
* search time.
|
|
538
|
+
*
|
|
539
|
+
* @defaultValue 4000
|
|
540
|
+
*/
|
|
541
|
+
maxCandidates?: number;
|
|
542
|
+
/**
|
|
543
|
+
* Maximum number of chords {@link voiceProgression} will voice. The search
|
|
544
|
+
* per chord is bounded internally, so the cost of a progression is linear in
|
|
545
|
+
* its length; this is the guard against an unbounded caller, not a limit on
|
|
546
|
+
* the search.
|
|
547
|
+
*
|
|
548
|
+
* @defaultValue 1000000
|
|
549
|
+
*/
|
|
550
|
+
budget?: number;
|
|
551
|
+
/**
|
|
552
|
+
* The prevailing key, as a key name, a key/scale, or a `Key`. Supplying it
|
|
553
|
+
* enables the rules that only make sense relative to a tonic: the leading
|
|
554
|
+
* tone is neither doubled nor left unresolved. Without it, voicings are
|
|
555
|
+
* chosen from chord structure and voice-leading distance alone.
|
|
556
|
+
*/
|
|
557
|
+
key?: KeyLike;
|
|
558
|
+
/**
|
|
559
|
+
* The chord that produced the current voicing passed to {@link nextVoicing},
|
|
560
|
+
* as a chord symbol, chord data, or a `Chord`. When supplied, chordal-seventh
|
|
561
|
+
* resolution is scored exactly as it is by {@link voiceProgression}.
|
|
562
|
+
*/
|
|
563
|
+
previousChord?: ChordLike;
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Realize a single chord as one MIDI pitch per voice, ascending (index 0 =
|
|
567
|
+
* lowest). The bass voice takes the chord's `bassPc` when set, otherwise the
|
|
568
|
+
* root; upper voices take chord pitch classes, doubling the root or fifth as
|
|
569
|
+
* needed to fill all voices. The result stays inside each voice's range, keeps
|
|
570
|
+
* adjacent upper voices within `maxSpacing`, avoids voice crossing, and is
|
|
571
|
+
* deterministic: a compact close-position voicing centered in the ranges.
|
|
572
|
+
*
|
|
573
|
+
* @param chord The chord to voice.
|
|
574
|
+
* @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
|
|
575
|
+
* @returns MIDI pitches, ascending, one per voice.
|
|
576
|
+
* @throws If no voicing fits the given ranges.
|
|
577
|
+
* @example
|
|
578
|
+
* ```ts
|
|
579
|
+
* import { parseChordSymbol, voiceChord } from '@libraz/libcantus';
|
|
580
|
+
* const chord = parseChordSymbol('Cmaj7');
|
|
581
|
+
* voiceChord(chord); // four ascending MIDI pitches within the SATB ranges
|
|
582
|
+
* ```
|
|
583
|
+
* @category Voicing & Counterpoint
|
|
584
|
+
*/
|
|
585
|
+
declare function voiceChord(chord: ChordLike, opts?: VoicingOptions): number[];
|
|
586
|
+
/**
|
|
587
|
+
* Voice a chord progression with smooth, bounded SATB-style leading.
|
|
588
|
+
*
|
|
589
|
+
* Each chord is chosen from a deterministic candidate set, minimizing motion
|
|
590
|
+
* while penalizing counterpoint violations. A supplied key additionally
|
|
591
|
+
* resolves leading tones and chordal sevenths.
|
|
592
|
+
*
|
|
593
|
+
* @param chords The chords to voice in order.
|
|
594
|
+
* @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
|
|
595
|
+
* @returns One ascending MIDI voicing per chord.
|
|
596
|
+
* @throws {@link NoSolutionError} with the failing chord index when a chord
|
|
597
|
+
* cannot fit the requested ranges.
|
|
598
|
+
* @category Voicing & Counterpoint
|
|
599
|
+
*/
|
|
600
|
+
declare function voiceProgression(chords: readonly ChordLike[], opts?: VoicingOptions): number[][];
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* A tertian voicing style for {@link voiceChordStyled}.
|
|
604
|
+
*
|
|
605
|
+
* - `close`: the plain close-position tertian stack.
|
|
606
|
+
* - `drop2`: the second voice from the top dropped an octave (drop-2 voicing).
|
|
607
|
+
* - `drop3`: the third voice from the top dropped an octave (drop-3 voicing).
|
|
608
|
+
* - `shell`: root plus guide tones (third and seventh) for seventh chords, or
|
|
609
|
+
* root/third/fifth for triads; the fifth and tensions are omitted.
|
|
610
|
+
* - `rootless`: the root omitted, keeping third/fifth/seventh and tensions
|
|
611
|
+
* (a typical left-hand jazz voicing).
|
|
612
|
+
*
|
|
613
|
+
* @category Voicing & Counterpoint
|
|
614
|
+
*/
|
|
615
|
+
type VoicingStyle = 'close' | 'drop2' | 'drop3' | 'shell' | 'rootless';
|
|
616
|
+
/**
|
|
617
|
+
* Options controlling {@link voiceChordStyled}.
|
|
618
|
+
*
|
|
619
|
+
* @category Voicing & Counterpoint
|
|
620
|
+
*/
|
|
621
|
+
type StyledVoicingOptions = {
|
|
622
|
+
/**
|
|
623
|
+
* Voicing style to build.
|
|
624
|
+
*
|
|
625
|
+
* @defaultValue 'close'
|
|
626
|
+
*/
|
|
627
|
+
style?: VoicingStyle;
|
|
628
|
+
/** Constrain the highest voice to this pitch class (0..11) when given. */
|
|
629
|
+
topNote?: number;
|
|
630
|
+
/**
|
|
631
|
+
* Base octave in scientific pitch notation: octave 4 starts the stack near
|
|
632
|
+
* middle C (MIDI 60), i.e. near `12 * (octave + 1)`.
|
|
633
|
+
*
|
|
634
|
+
* @defaultValue 4
|
|
635
|
+
*/
|
|
636
|
+
octave?: number;
|
|
637
|
+
/**
|
|
638
|
+
* Omit the root regardless of style (relevant for jazz voicings).
|
|
639
|
+
*
|
|
640
|
+
* @defaultValue false
|
|
641
|
+
*/
|
|
642
|
+
rootless?: boolean;
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Realize a single chord as an explicit tertian voicing in a chosen style,
|
|
646
|
+
* independent of the SATB range search used by {@link voiceChord}.
|
|
647
|
+
*
|
|
648
|
+
* The chord tones are stacked in close position from the bass (`bassPc`, else
|
|
649
|
+
* `rootPc`) in the given scientific-pitch `octave` upward (octave 4 starts the
|
|
650
|
+
* bass near C4 = MIDI 60, matching {@link midiToNote} and the bass module).
|
|
651
|
+
* When `topNote` is given the stack is rotated so the highest voice's pitch
|
|
652
|
+
* class equals that note (or the nearest chord tone). The chosen
|
|
653
|
+
* {@link VoicingStyle} then transforms the stack, and the result is returned as
|
|
654
|
+
* ascending MIDI pitches (index 0 = lowest).
|
|
655
|
+
*
|
|
656
|
+
* Every returned pitch is a valid MIDI number: an octave whose stack would run
|
|
657
|
+
* off either end of the 0..127 range is rejected rather than voiced out of
|
|
658
|
+
* range, matching {@link nextVoicing}, which clamps its derived ranges.
|
|
659
|
+
*
|
|
660
|
+
* @param chord The chord to voice.
|
|
661
|
+
* @param opts Styled voicing options; defaults to a close voicing at octave 4.
|
|
662
|
+
* @returns MIDI pitches, ascending, one per retained voice.
|
|
663
|
+
* @throws If the voicing would not fit inside MIDI 0..127 at the given octave.
|
|
664
|
+
* @example
|
|
665
|
+
* ```ts
|
|
666
|
+
* import { parseChordSymbol, voiceChordStyled } from '@libraz/libcantus';
|
|
667
|
+
* const chord = parseChordSymbol('Dm7');
|
|
668
|
+
* voiceChordStyled(chord, { style: 'drop2' }); // ascending MIDI pitches, drop-2 voicing
|
|
669
|
+
* ```
|
|
670
|
+
* @category Voicing & Counterpoint
|
|
671
|
+
*/
|
|
672
|
+
declare function voiceChordStyled(chord: Chord, opts?: StyledVoicingOptions): number[];
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Species counterpoint: mark a two-voice exercise against the sixteenth-century
|
|
676
|
+
* rules.
|
|
677
|
+
*
|
|
678
|
+
* This is the conservatory exercise, not a general style checker. It grades a
|
|
679
|
+
* counterpoint written above or below a given cantus firmus in one of the five
|
|
680
|
+
* species, and reports what it finds as the same {@link PartWritingViolation}
|
|
681
|
+
* the four-part checker uses.
|
|
682
|
+
*
|
|
683
|
+
* Both voices arrive as spelled notes, because half of what the rules forbid is
|
|
684
|
+
* invisible in a pitch: the augmented second of the harmonic minor is a minor
|
|
685
|
+
* third by ear, and the diminished fourth that may not stand as a consonance is
|
|
686
|
+
* a major third by ear.
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Which species an exercise is written in: note against note, two, three or
|
|
691
|
+
* four notes against one, suspensions, or the florid mixture of them all.
|
|
692
|
+
*
|
|
693
|
+
* @category Voicing & Counterpoint
|
|
694
|
+
*/
|
|
695
|
+
type Species = 1 | 2 | 3 | 4 | 5;
|
|
696
|
+
/**
|
|
697
|
+
* Options controlling {@link checkSpecies}.
|
|
698
|
+
*
|
|
699
|
+
* @category Voicing & Counterpoint
|
|
700
|
+
*/
|
|
701
|
+
type SpeciesOptions = {
|
|
702
|
+
/**
|
|
703
|
+
* Length of each counterpoint note, measured in cantus-firmus notes: 1 is a
|
|
704
|
+
* whole note filling a measure, 0.5 a half, 0.25 a quarter.
|
|
705
|
+
*
|
|
706
|
+
* The first four species have a fixed ratio, so the durations are implied by
|
|
707
|
+
* the note count and this is only needed to describe something unusual. The
|
|
708
|
+
* fifth species mixes note values by definition and cannot be read without it.
|
|
709
|
+
*/
|
|
710
|
+
durations?: readonly number[];
|
|
711
|
+
/**
|
|
712
|
+
* Which side of the cantus firmus the counterpoint is written on. Defaults to
|
|
713
|
+
* whichever side it actually lies on, by mean pitch.
|
|
714
|
+
*/
|
|
715
|
+
counterpointAbove?: boolean;
|
|
716
|
+
};
|
|
717
|
+
/**
|
|
718
|
+
* Check a species counterpoint exercise and report every rule it breaks.
|
|
719
|
+
*
|
|
720
|
+
* The cantus firmus is one note per measure. The counterpoint is aligned to it
|
|
721
|
+
* by position rather than by written rhythm: the first four species have a fixed
|
|
722
|
+
* number of notes per measure, so the note count alone says where each note
|
|
723
|
+
* falls, and a closing measure written as a single whole note is accepted as the
|
|
724
|
+
* convention it is. The fifth species mixes note values and therefore needs
|
|
725
|
+
* `opts.durations`, given in cantus-firmus notes.
|
|
726
|
+
*
|
|
727
|
+
* Judged are the vertical intervals under each species' dissonance licence
|
|
728
|
+
* (none in the first, a passing dissonance on the weak half in the second and
|
|
729
|
+
* third, a prepared suspension on the downbeat in the fourth, both in the
|
|
730
|
+
* fifth; the third and fifth species also write the *nota cambiata* and the
|
|
731
|
+
* double neighbour, which quit a dissonance by leap), parallel and hidden
|
|
732
|
+
* perfects and the *battuta* between the voices, augmented and otherwise
|
|
733
|
+
* forbidden leaps along the counterpoint, the shape of the counterpoint as a
|
|
734
|
+
* line, and the opening and closing formulas. Violations name the cantus firmus
|
|
735
|
+
* as voice 0 and the counterpoint as voice 1, and index into the counterpoint.
|
|
736
|
+
*
|
|
737
|
+
* @param cantusFirmus The given voice, one spelled note per measure.
|
|
738
|
+
* @param counterpoint The written voice, in spelled notes.
|
|
739
|
+
* @param species Which species the exercise is written in.
|
|
740
|
+
* @param mode The mode the exercise is in.
|
|
741
|
+
* @param opts Note lengths, and which side the counterpoint is written on.
|
|
742
|
+
* @returns Every violation found, in the order the exercise commits them; an
|
|
743
|
+
* empty array for a clean exercise.
|
|
744
|
+
* @throws If the species is not one of the five, either voice is empty, a note
|
|
745
|
+
* carries no octave, or the fifth species is given without durations.
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* import { checkSpecies, majorKey, parseNote } from '@libraz/libcantus';
|
|
749
|
+
* const cf = ['C4', 'D4', 'E4', 'D4', 'C4'].map((n) => parseNote(n));
|
|
750
|
+
* const cp = ['C5', 'A4', 'G4', 'B4', 'C5'].map((n) => parseNote(n));
|
|
751
|
+
* checkSpecies(cf, cp, 1, majorKey(0)); // [] — a clean first-species exercise
|
|
752
|
+
* ```
|
|
753
|
+
* @category Voicing & Counterpoint
|
|
754
|
+
*/
|
|
755
|
+
declare function checkSpecies(cantusFirmus: readonly Note[], counterpoint: readonly Note[], species: Species, mode: KeyScale, opts?: SpeciesOptions): PartWritingViolation[];
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Part writing: grade a finished exercise against the classical four-part rules.
|
|
759
|
+
*
|
|
760
|
+
* The counterpoint predicates each judge one pair of voices at one moment; this
|
|
761
|
+
* module walks a whole progression and reports every violation it finds, so a
|
|
762
|
+
* completed exercise can be marked in a single call.
|
|
763
|
+
*
|
|
764
|
+
* The input is spelled notes rather than MIDI pitches because two of the rules
|
|
765
|
+
* cannot be decided without them: a cross relation is one letter carrying two
|
|
766
|
+
* different accidentals, and an augmented second is a minor third under another
|
|
767
|
+
* name. A caller holding only pitches spells them with {@link spellVoicing}.
|
|
768
|
+
*/
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* The rule a {@link PartWritingViolation} breaks.
|
|
772
|
+
*
|
|
773
|
+
* - `parallelFifth`, `parallelOctave`: consecutive perfect intervals of the same
|
|
774
|
+
* kind between one pair of voices. A parallel unison is reported as a
|
|
775
|
+
* `parallelOctave`, since it is the same perfect class.
|
|
776
|
+
* - `hiddenPerfect`: the outer voices reach a perfect fifth or octave by similar
|
|
777
|
+
* motion with a leap in the upper voice.
|
|
778
|
+
* - `crossRelation`: one letter carries two different accidentals in two
|
|
779
|
+
* different voices across a chord change, exposed — neither note led into by
|
|
780
|
+
* step, at least one of them in an outer voice, and neither chord one of the
|
|
781
|
+
* chromatic harmonies that are written with the contradiction.
|
|
782
|
+
* - `voiceCrossing`, `overlap`, `spacing`, `range`: the texture rules.
|
|
783
|
+
* - `unresolvedLeadingTone`, `unresolvedSeventh`: a tendency tone that does not
|
|
784
|
+
* go where it must.
|
|
785
|
+
* - `augmentedMelodicInterval`: a single voice moves by an augmented interval.
|
|
786
|
+
*
|
|
787
|
+
* The rest are raised by {@link checkSpecies} alone, since they describe faults
|
|
788
|
+
* only a species exercise can commit:
|
|
789
|
+
*
|
|
790
|
+
* - `wrongRhythmicRatio`: the counterpoint does not present the number of notes
|
|
791
|
+
* per cantus-firmus note its species requires.
|
|
792
|
+
* - `unpreparedDissonance`: a dissonance falls where the species allows none, or
|
|
793
|
+
* is reached or left by leap where it must be passed through by step.
|
|
794
|
+
* - `unresolvedSuspension`: a fourth-species suspension that does not fall by
|
|
795
|
+
* step onto the following consonance.
|
|
796
|
+
* - `illegalLeap`: a melodic leap the style forbids — either seventh, the
|
|
797
|
+
* tritone, or anything wider than an octave.
|
|
798
|
+
* - `battuta`: an octave or unison reached by contrary motion with the upper
|
|
799
|
+
* voice leaping down.
|
|
800
|
+
* - `missingCadence`: the exercise does not open or close on the formula its
|
|
801
|
+
* species requires.
|
|
802
|
+
* - `melodicShape`: the counterpoint is a poor line — it has no single climax,
|
|
803
|
+
* leaps too often or too far one way, leaves a wide leap unanswered, outlines
|
|
804
|
+
* a tritone, or repeats a note where the species keeps moving. The rationale
|
|
805
|
+
* says which.
|
|
806
|
+
*
|
|
807
|
+
* @category Voicing & Counterpoint
|
|
808
|
+
*/
|
|
809
|
+
type PartWritingViolationKind = 'parallelFifth' | 'parallelOctave' | 'hiddenPerfect' | 'crossRelation' | 'voiceCrossing' | 'overlap' | 'spacing' | 'range' | 'unresolvedLeadingTone' | 'unresolvedSeventh' | 'augmentedMelodicInterval' | 'wrongRhythmicRatio' | 'unpreparedDissonance' | 'unresolvedSuspension' | 'illegalLeap' | 'battuta' | 'missingCadence' | 'melodicShape';
|
|
810
|
+
/**
|
|
811
|
+
* One broken rule, located in the progression and in the texture.
|
|
812
|
+
*
|
|
813
|
+
* @category Voicing & Counterpoint
|
|
814
|
+
*/
|
|
815
|
+
type PartWritingViolation = {
|
|
816
|
+
/** The rule that was broken. */
|
|
817
|
+
kind: PartWritingViolationKind;
|
|
818
|
+
/**
|
|
819
|
+
* The voices involved, by index into a voicing (0 = lowest). A rule about a
|
|
820
|
+
* pair names both, ascending; a rule about a single line names one. A
|
|
821
|
+
* `crossRelation` is the exception: it names the voice holding the earlier
|
|
822
|
+
* note first, whichever is higher. In a species exercise there are only two
|
|
823
|
+
* voices and they are named by role instead: 0 is the cantus firmus and 1 the
|
|
824
|
+
* counterpoint, whichever lies above.
|
|
825
|
+
*/
|
|
826
|
+
voices: number[];
|
|
827
|
+
/**
|
|
828
|
+
* Index of the chord the motion starts on; in a species exercise, the index
|
|
829
|
+
* into the counterpoint.
|
|
830
|
+
*/
|
|
831
|
+
fromIndex: number;
|
|
832
|
+
/** Index of the chord it ends on; equal to `fromIndex` for a vertical rule. */
|
|
833
|
+
toIndex: number;
|
|
834
|
+
/** A short sentence naming the musical reason. */
|
|
835
|
+
rationale: string;
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* A chord realized as one spelled note per voice, ascending (index 0 = lowest),
|
|
839
|
+
* matching the voice order {@link voiceChord} and {@link SATB_RANGES} use.
|
|
840
|
+
*
|
|
841
|
+
* Every note must carry an octave: the rules measure real distances between
|
|
842
|
+
* sounding pitches, which a bare pitch class does not have.
|
|
843
|
+
*
|
|
844
|
+
* @category Voicing & Counterpoint
|
|
845
|
+
*/
|
|
846
|
+
type SpelledVoicing = Note[];
|
|
847
|
+
/**
|
|
848
|
+
* Options controlling {@link checkPartWriting}.
|
|
849
|
+
*
|
|
850
|
+
* @category Voicing & Counterpoint
|
|
851
|
+
*/
|
|
852
|
+
type PartWritingOptions = {
|
|
853
|
+
/**
|
|
854
|
+
* Per-voice ranges, ascending (index 0 = lowest), against which the `range`
|
|
855
|
+
* rule is judged. Defaults to {@link SATB_RANGES} for a four-voice exercise;
|
|
856
|
+
* for any other voice count there is no conventional compass to assume, so
|
|
857
|
+
* the rule is skipped unless ranges are given. Given ranges must cover every
|
|
858
|
+
* voice of the widest chord, and each must be finite with `min` no greater
|
|
859
|
+
* than `max`.
|
|
860
|
+
*/
|
|
861
|
+
ranges?: VoiceRange[];
|
|
862
|
+
/**
|
|
863
|
+
* Maximum spacing in semitones between adjacent upper voices. The bass-tenor
|
|
864
|
+
* pair is exempt, as convention has it. Must be finite and non-negative.
|
|
865
|
+
*
|
|
866
|
+
* @defaultValue 12
|
|
867
|
+
*/
|
|
868
|
+
maxSpacing?: number;
|
|
869
|
+
};
|
|
870
|
+
/**
|
|
871
|
+
* Spell a voicing given as MIDI pitches, so it can be checked by
|
|
872
|
+
* {@link checkPartWriting}.
|
|
873
|
+
*
|
|
874
|
+
* Each pitch is named against the chord sounding under it and then the key, so a
|
|
875
|
+
* chord tone takes the letter its interval above the root implies: the third of
|
|
876
|
+
* a D major chord in C major spells F#, not Gb. The octave is kept, since the
|
|
877
|
+
* rules measure real distances.
|
|
878
|
+
*
|
|
879
|
+
* @param voicing MIDI pitches, ascending (index 0 = lowest).
|
|
880
|
+
* @param chord The chord sounding, supplying the enharmonic evidence.
|
|
881
|
+
* @param key The key the exercise is written in.
|
|
882
|
+
* @returns One spelled note per pitch, in the same order.
|
|
883
|
+
* @example
|
|
884
|
+
* ```ts
|
|
885
|
+
* import { majorKey, makeChord, noteNames, spellVoicing } from '@libraz/libcantus';
|
|
886
|
+
* noteNames(spellVoicing([50, 57, 66, 69], makeChord(2, 'maj'), majorKey(0)));
|
|
887
|
+
* // ['D3', 'A3', 'F#4', 'A4'] — the third of D spells F# in C major
|
|
888
|
+
* ```
|
|
889
|
+
* @category Voicing & Counterpoint
|
|
890
|
+
*/
|
|
891
|
+
declare function spellVoicing(voicing: readonly number[], chord: Chord, key: KeyScale): SpelledVoicing;
|
|
892
|
+
/**
|
|
893
|
+
* Check a finished part-writing exercise and report every rule it breaks.
|
|
894
|
+
*
|
|
895
|
+
* The voicings are given one per chord, each ascending with one spelled note per
|
|
896
|
+
* voice (index 0 = lowest, the bass), the voice order {@link voiceChord} and
|
|
897
|
+
* {@link SATB_RANGES} already use. Notes must carry octaves.
|
|
898
|
+
*
|
|
899
|
+
* Each chord is checked for voice crossing, over-wide spacing between adjacent
|
|
900
|
+
* upper voices, and voices outside their range; each pair of consecutive chords
|
|
901
|
+
* for parallel and hidden perfects, voice overlap, cross relations, augmented
|
|
902
|
+
* melodic intervals, and unresolved tendency tones. Violations come back in
|
|
903
|
+
* musical order: the rules inside a chord, then the rules taking it to the next.
|
|
904
|
+
*
|
|
905
|
+
* @param voicings One voicing per chord, ascending, in spelled notes.
|
|
906
|
+
* @param chords The chords those voicings realize, in the same order.
|
|
907
|
+
* @param key The key the exercise is written in; it names the leading tone.
|
|
908
|
+
* @param opts Ranges and the upper-voice spacing limit. Both are validated
|
|
909
|
+
* before any rule runs: an empty result has to mean that nothing was broken,
|
|
910
|
+
* never that a rule could not be evaluated.
|
|
911
|
+
* @returns Every violation found, in musical order; an empty array for a clean
|
|
912
|
+
* exercise.
|
|
913
|
+
* @throws If the two arrays differ in length, a note carries no octave, the
|
|
914
|
+
* spacing limit is not a non-negative finite number, or the ranges are empty,
|
|
915
|
+
* malformed, or too few for the voices being checked.
|
|
916
|
+
* @example
|
|
917
|
+
* ```ts
|
|
918
|
+
* import { checkPartWriting, majorKey, makeChord, spellVoicing } from '@libraz/libcantus';
|
|
919
|
+
* const key = majorKey(0);
|
|
920
|
+
* const chords = [makeChord(0, 'maj'), makeChord(2, 'min')];
|
|
921
|
+
* const voicings = [[48, 55, 64, 72], [50, 57, 65, 69]].map((pitches, i) =>
|
|
922
|
+
* spellVoicing(pitches, chords[i] ?? chords[0], key),
|
|
923
|
+
* );
|
|
924
|
+
* const violations = checkPartWriting(voicings, chords, key);
|
|
925
|
+
* violations.map((violation) => violation.kind); // ['parallelFifth']
|
|
926
|
+
* violations[0]?.voices; // [0, 1]
|
|
927
|
+
* ```
|
|
928
|
+
* @category Voicing & Counterpoint
|
|
929
|
+
*/
|
|
930
|
+
declare function checkPartWriting(voicings: readonly SpelledVoicing[], chords: readonly Chord[], key: KeyScale, opts?: PartWritingOptions): PartWritingViolation[];
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Transposing instruments: the fixed interval between the pitch a player reads
|
|
934
|
+
* and the pitch the instrument sounds.
|
|
935
|
+
*
|
|
936
|
+
* The distance is a spelled interval, never a semitone count. A clarinet in A
|
|
937
|
+
* sounds a minor third below what it reads, so a written C sounds A, a written
|
|
938
|
+
* D sounds B, and a written D# sounds B#: the letter always moves by the
|
|
939
|
+
* interval's diatonic number, where a bare count of three semitones would take
|
|
940
|
+
* the last of those to C natural and lose the letter the part is written on.
|
|
941
|
+
*
|
|
942
|
+
* The octave is part of that interval rather than folded away into a pitch
|
|
943
|
+
* class, so instruments pitched in the same key stay apart: the alto saxophone
|
|
944
|
+
* sounds a major sixth lower, the baritone saxophone a major thirteenth (the
|
|
945
|
+
* same key an octave further down), and the E flat clarinet a minor third
|
|
946
|
+
* higher. The pure octave transposers — piccolo, double bass, contrabassoon,
|
|
947
|
+
* guitar — are a perfect octave, a unison-class interval displaced by one
|
|
948
|
+
* octave.
|
|
949
|
+
*/
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* The interval from a written pitch to the pitch the instrument sounds, one
|
|
953
|
+
* entry per built-in instrument.
|
|
954
|
+
*
|
|
955
|
+
* Each value is an interval name as {@link parseInterval} reads it, in the
|
|
956
|
+
* written-to-sounding direction: a leading `'-'` marks an instrument that
|
|
957
|
+
* sounds below what it reads, which is most of them. A transposition wider than
|
|
958
|
+
* an octave is named by its compound interval rather than reduced, because the
|
|
959
|
+
* octave is exactly what separates the tenor saxophone from the soprano and the
|
|
960
|
+
* baritone from the alto.
|
|
961
|
+
*
|
|
962
|
+
* @example
|
|
963
|
+
* ```ts
|
|
964
|
+
* import { TRANSPOSING_INSTRUMENTS } from '@libraz/libcantus';
|
|
965
|
+
* TRANSPOSING_INSTRUMENTS.clarinetA; // '-m3' — sounds a minor third lower
|
|
966
|
+
* TRANSPOSING_INSTRUMENTS.piccolo; // 'P8' — sounds an octave higher
|
|
967
|
+
* ```
|
|
968
|
+
* @category Pitch & Intervals
|
|
969
|
+
*/
|
|
970
|
+
declare const TRANSPOSING_INSTRUMENTS: Readonly<{
|
|
971
|
+
clarinetBb: "-M2";
|
|
972
|
+
trumpetBb: "-M2";
|
|
973
|
+
sopranoSax: "-M2";
|
|
974
|
+
tenorSax: "-M9";
|
|
975
|
+
bassClarinet: "-M9";
|
|
976
|
+
clarinetA: "-m3";
|
|
977
|
+
hornF: "-P5";
|
|
978
|
+
englishHorn: "-P5";
|
|
979
|
+
clarinetEb: "m3";
|
|
980
|
+
altoSax: "-M6";
|
|
981
|
+
baritoneSax: "-M13";
|
|
982
|
+
trumpetD: "M2";
|
|
983
|
+
piccolo: "P8";
|
|
984
|
+
doubleBass: "-P8";
|
|
985
|
+
contrabassoon: "-P8";
|
|
986
|
+
guitar: "-P8";
|
|
987
|
+
}>;
|
|
988
|
+
/**
|
|
989
|
+
* The name of a built-in transposing instrument, as
|
|
990
|
+
* {@link TRANSPOSING_INSTRUMENTS} keys them.
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```ts
|
|
994
|
+
* import type { TransposingInstrumentName } from '@libraz/libcantus';
|
|
995
|
+
* const part: TransposingInstrumentName = 'clarinetA';
|
|
996
|
+
* ```
|
|
997
|
+
* @category Pitch & Intervals
|
|
998
|
+
*/
|
|
999
|
+
type TransposingInstrumentName = keyof typeof TRANSPOSING_INSTRUMENTS;
|
|
1000
|
+
/**
|
|
1001
|
+
* A transposing instrument: one of the built-in names, or a spelled interval
|
|
1002
|
+
* for an instrument the table does not carry.
|
|
1003
|
+
*
|
|
1004
|
+
* An interval is read in the same written-to-sounding direction as the table,
|
|
1005
|
+
* so `'-M2'` is any instrument in B flat and `'P8'` any that sounds an octave
|
|
1006
|
+
* up. The names complete in an editor while any other string is still accepted,
|
|
1007
|
+
* which is what lets an instrument read from configuration go straight in.
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```ts
|
|
1011
|
+
* import type { TransposingInstrument } from '@libraz/libcantus';
|
|
1012
|
+
* const clarinet: TransposingInstrument = 'clarinetA';
|
|
1013
|
+
* const altoFlute: TransposingInstrument = '-P4';
|
|
1014
|
+
* ```
|
|
1015
|
+
* @category Pitch & Intervals
|
|
1016
|
+
*/
|
|
1017
|
+
type TransposingInstrument = TransposingInstrumentName | (string & {}) | Exclude<IntervalLike, string>;
|
|
1018
|
+
/**
|
|
1019
|
+
* The interval a transposing instrument sounds at, measured from what it reads.
|
|
1020
|
+
*
|
|
1021
|
+
* This is the table's own direction: the answer descends for an instrument that
|
|
1022
|
+
* sounds below the printed part, which is most of them. Applying it to a
|
|
1023
|
+
* written pitch is {@link toSoundingPitch}, and applying it to a concert key is
|
|
1024
|
+
* how a written key is read back as the key it sounds in.
|
|
1025
|
+
*
|
|
1026
|
+
* @param instrument A built-in instrument name, or an interval naming a
|
|
1027
|
+
* transposition the table does not carry.
|
|
1028
|
+
* @returns The written-to-sounding interval.
|
|
1029
|
+
* @throws If the value is neither a known instrument nor a spelled interval.
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```ts
|
|
1032
|
+
* import { instrumentTransposition } from '@libraz/libcantus';
|
|
1033
|
+
* instrumentTransposition('clarinetA');
|
|
1034
|
+
* // { number: 3, quality: 'm', semitones: -3, descending: true }
|
|
1035
|
+
* instrumentTransposition('piccolo'); // { number: 8, quality: 'P', semitones: 12 }
|
|
1036
|
+
* ```
|
|
1037
|
+
* @category Pitch & Intervals
|
|
1038
|
+
*/
|
|
1039
|
+
declare function instrumentTransposition(instrument: TransposingInstrument): SpelledInterval;
|
|
1040
|
+
/**
|
|
1041
|
+
* The pitch an instrument sounds for a written note.
|
|
1042
|
+
*
|
|
1043
|
+
* The interval decides the letter, so the spelling of the part survives the
|
|
1044
|
+
* conversion: on a clarinet in A a written C sounds A, a written D sounds B,
|
|
1045
|
+
* and a written D# sounds B# — a semitone count alone could answer C natural
|
|
1046
|
+
* for that last one and lose the letter the part is written on. A note carrying
|
|
1047
|
+
* an octave moves register with it — a written C4 sounds A3 — while an
|
|
1048
|
+
* octave-less note stays octave-less, so a pure octave transposer leaves it
|
|
1049
|
+
* alone.
|
|
1050
|
+
*
|
|
1051
|
+
* @param note The written note, as the player reads it: a note name, a MIDI
|
|
1052
|
+
* number, note data, or a `Note`.
|
|
1053
|
+
* @param instrument A built-in instrument name, or an interval naming a
|
|
1054
|
+
* transposition the table does not carry.
|
|
1055
|
+
* @returns The sounding note, at concert pitch.
|
|
1056
|
+
* @throws If the note is malformed, or the instrument is neither a known name
|
|
1057
|
+
* nor a spelled interval.
|
|
1058
|
+
* @example
|
|
1059
|
+
* ```ts
|
|
1060
|
+
* import { formatNote, parseNote, toSoundingPitch } from '@libraz/libcantus';
|
|
1061
|
+
* formatNote(toSoundingPitch(parseNote('C4'), 'clarinetA')); // 'A3'
|
|
1062
|
+
* formatNote(toSoundingPitch('D#4', 'clarinetA')); // 'B#3'
|
|
1063
|
+
* formatNote(toSoundingPitch(parseNote('C4'), 'piccolo')); // 'C5'
|
|
1064
|
+
* ```
|
|
1065
|
+
* @category Pitch & Intervals
|
|
1066
|
+
*/
|
|
1067
|
+
declare function toSoundingPitch(note: NoteLike, instrument: TransposingInstrument): Note;
|
|
1068
|
+
/**
|
|
1069
|
+
* The note an instrument must read to sound a given concert pitch.
|
|
1070
|
+
*
|
|
1071
|
+
* The exact inverse of {@link toSoundingPitch} for every instrument: the same
|
|
1072
|
+
* interval applied the other way, so the letter and the octave both come back.
|
|
1073
|
+
* A concert A3 is written C4 for a clarinet in A, and a concert E flat 3 is
|
|
1074
|
+
* written C4 for an alto saxophone but C5 for a baritone saxophone, which reads
|
|
1075
|
+
* the same key an octave lower.
|
|
1076
|
+
*
|
|
1077
|
+
* @param note The sounding note, at concert pitch: a note name, a MIDI number,
|
|
1078
|
+
* note data, or a `Note`.
|
|
1079
|
+
* @param instrument A built-in instrument name, or an interval naming a
|
|
1080
|
+
* transposition the table does not carry.
|
|
1081
|
+
* @returns The written note, as the player reads it.
|
|
1082
|
+
* @throws If the note is malformed, or the instrument is neither a known name
|
|
1083
|
+
* nor a spelled interval.
|
|
1084
|
+
* @example
|
|
1085
|
+
* ```ts
|
|
1086
|
+
* import { formatNote, parseNote, toWrittenPitch } from '@libraz/libcantus';
|
|
1087
|
+
* formatNote(toWrittenPitch(parseNote('A3'), 'clarinetA')); // 'C4'
|
|
1088
|
+
* formatNote(toWrittenPitch('Eb3', 'altoSax')); // 'C4'
|
|
1089
|
+
* formatNote(toWrittenPitch(parseNote('Eb3'), 'baritoneSax')); // 'C5'
|
|
1090
|
+
* ```
|
|
1091
|
+
* @category Pitch & Intervals
|
|
1092
|
+
*/
|
|
1093
|
+
declare function toWrittenPitch(note: NoteLike, instrument: TransposingInstrument): Note;
|
|
1094
|
+
|
|
1095
|
+
export { type AvailableTensionsOptions as A, keySignatureFifths as B, type ChordScaleMatch as C, scaleMatchesChord as D, scaleSystemOf as E, scalesForChanges as F, spellVoicing as G, supportsFunctionalHarmony as H, toSoundingPitch as I, toWrittenPitch as J, type KeyMode as K, voiceChord as L, voiceChordStyled as M, voiceIndependence as N, voiceProgression as O, type PartWritingOptions as P, SATB_RANGES as S, TRANSPOSING_INSTRUMENTS as T, type VoiceIndependenceOptions as V, type AvoidNoteUse as a, type AvoidNotesOptions as b, type ChordScaleReportEntry as c, type PartWritingViolation as d, type PartWritingViolationKind as e, SCALE_SYSTEMS as f, type ScaleChoice as g, type ScaleSystem as h, type Species as i, type SpeciesOptions as j, type SpelledVoicing as k, type StyledVoicingOptions as l, type TransposingInstrument as m, type TransposingInstrumentName as n, type VoiceIndependenceReport as o, type VoiceRange as p, type VoicingOptions as q, type VoicingStyle as r, availableTensions as s, avoidNotes as t, checkPartWriting as u, checkSpecies as v, chordScaleReport as w, chordScales as x, instrumentTransposition as y, keyFromFifths as z };
|