@libraz/libcantus 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -261
- package/README_ja.md +115 -0
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +213 -456
- package/dist/analyze/index.d.ts +213 -456
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/chunk-ELCTZUJJ.js +379 -0
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +103 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +96 -345
- package/dist/core/index.d.ts +96 -345
- package/dist/core/index.js +104 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +437 -1095
- package/dist/generate/index.d.ts +437 -1095
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +358 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +407 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4685 -243
- package/dist/model/index.d.ts +4685 -243
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +513 -173
- package/dist/theory/index.d.ts +513 -173
- package/dist/theory/index.js +118 -24
- package/package.json +16 -11
- package/dist/chunk-36N73KZ3.cjs +0 -246
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs +0 -3077
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs +0 -498
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js +0 -3077
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs +0 -679
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js +0 -479
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js +0 -1449
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs +0 -30
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs +0 -479
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js +0 -173
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-HKGYCQ34.js +0 -53
- package/dist/chunk-HKGYCQ34.js.map +0 -1
- package/dist/chunk-I24MV65U.js +0 -106
- package/dist/chunk-I24MV65U.js.map +0 -1
- package/dist/chunk-KMHOICIY.js +0 -679
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs +0 -1117
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NKIBQ6PT.js +0 -30
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs +0 -106
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs +0 -1449
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js +0 -327
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js +0 -845
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs +0 -327
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs +0 -845
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js +0 -1117
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-RUMU3UA6.cjs +0 -53
- package/dist/chunk-RUMU3UA6.cjs.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-XEBG3G53.js +0 -498
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js +0 -246
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs +0 -173
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- package/dist/function-BtiZb9i9.d.ts +0 -208
- package/dist/function-DNugAOrt.d.cts +0 -208
- package/dist/index-6VR8-kit.d.ts +0 -143
- package/dist/index-70p168VM.d.ts +0 -192
- package/dist/index-Be74mcon.d.cts +0 -152
- package/dist/index-BtXT6UQ0.d.cts +0 -192
- package/dist/index-C5UBbQ4G.d.ts +0 -152
- package/dist/index-C83mZI1P.d.ts +0 -242
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-OG65ocOr.d.cts +0 -272
- package/dist/index-OG65ocOr.d.ts +0 -272
- package/dist/index-tJhkmQgE.d.cts +0 -143
- package/dist/index-y_ak9_Hi.d.cts +0 -242
- package/dist/styled-DIRkk9Uz.d.ts +0 -506
- package/dist/styled-DJbkJQfj.d.cts +0 -506
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
package/dist/analyze/index.d.cts
CHANGED
|
@@ -1,501 +1,258 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export { b as
|
|
8
|
-
|
|
9
|
-
export { B as BorrowedSource, C as
|
|
1
|
+
import { h as NoteLike, f as Note, g as NoteEvent } from '../index-B9MNZboQ.cjs';
|
|
2
|
+
export { a as Articulation, b as IntervalQualityLabel, d as KeyScale, S as SpelledInterval } from '../index-B9MNZboQ.cjs';
|
|
3
|
+
export { M as MeterChange, b as MeterLike, c as MeterMap, T as TimeSignature } from '../index-BkGmlyI3.cjs';
|
|
4
|
+
import { K as KeyLike, b as Chord, c as ChordLike } from '../index-C_8o--VE.cjs';
|
|
5
|
+
export { d as ChordQuality, e as ChordSegment, g as ChordSpan, j as ChordToneRole, k as KeyRelation, N as NoteSafety, l as PitchSpelling, S as SafetyProfile, V as VoiceSnapshot } from '../index-C_8o--VE.cjs';
|
|
6
|
+
export { r as ScaleName } from '../masks-DYbE485J.cjs';
|
|
7
|
+
export { A as ArrangementAnalysis, a as ArrangementOptions, b as ArrangementSession, c as ArrangementTrack, C as ChordMatch, d as Conflict, e as ContourDirection, D as DetectChordOptions, f as DetectKeyOptions, E as ExtractMotifsOptions, F as FormSection, g as FormSectionOptions, H as Hypermeter, h as HypermeterOptions, K as KeyMatch, i as KeyVariant, M as MODAL_SCALE_NAMES, j as MelodicComparison, k as MelodicContour, l as MelodicContourShape, m as MelodicPhrase, n as ModalCandidate, o as ModalScaleName, p as MotifData, q as MotifOccurrence, r as MotifRelation, s as MotifRelationKind, P as Phrase, t as PhraseOptions, u as PhraseSignal, R as ReduceProgressionOptions, v as ReducedChord, w as ReductionBasis, x as ReductionLevel, S as StructuralCadence, T as TensionPoint, y as TrackAnalysis, z as TrackEdit, B as TrackRole, G as analyzeArrangement, I as compareMelodies, J as createArrangementSession, L as detectChord, N as detectChordBest, O as detectKey, Q as detectKeyBest, U as detectKeyFromNotes, V as extractMotifs, W as hypermeter, X as melodicContour, Y as melodicSimilarity, Z as motifFromNotes, _ as phrasesFromTimeline, $ as reduceProgression, a0 as relateMotifs, a1 as sectionsFromNotes, a2 as structuralCadences, a3 as tensionCurve, a4 as tensionCurveFrom } from '../index-BO6xE9YU.cjs';
|
|
8
|
+
import { e as ChordTimeline } from '../index-Dzr13q3d.cjs';
|
|
9
|
+
export { A as AnalyzeChordOptions, a as AnalyzedNote, B as BorrowedSource, C as CadenceHit, b as CadenceResult, c as ChordAnalysis, d as ChordSegmentation, f as ChordTimelineOptions, g as ChordTimelineResult, h as ChordToRomanOptions, D as DetectCadenceOptions, E as ExplainRomanOptions, H as HarmonicFunction, I as IdentifiedVoiceNote, K as KeyContext, i as KeyProfileName, j as KeyProfilePair, k as KeyRegion, l as KeyTimelineOptions, P as PivotChord, R as RejectedCandidate, m as RomanExplanation, S as SuspensionFigure, T as TheoryLabel, V as VoiceNote, n as analyzeChord, o as analyzeVoice, p as borrowedSource, q as chordTimelineFromChords, r as chordTimelineFromNotes, s as chordToRoman, t as detectCadence, u as detectCadences, v as detectModulations, w as explainRoman, x as functionOf, y as isBorrowedChord, z as isDiatonic, F as isMinorKey, G as keyTimelineFromNotes, J as pivotChords, L as prevailingKeyOf, M as romanToChord, N as secondaryDominant, O as secondaryDominantOf, Q as toVoiceNotes } from '../index-Dzr13q3d.cjs';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* Augmented-sixth chords: the Italian, French, and German sixths.
|
|
13
|
+
*
|
|
14
|
+
* All three stand on the lowered submediant (b6) in the bass and carry an
|
|
15
|
+
* augmented sixth above it, and all three are predominants whose two outer
|
|
16
|
+
* voices resolve outward onto the dominant. The family is defined by that bass
|
|
17
|
+
* and that interval rather than by a stack of thirds, so a chord is recognized
|
|
18
|
+
* here from the tones sounding over that bass rather than through the
|
|
19
|
+
* Roman-numeral degree tables.
|
|
20
|
+
*
|
|
21
|
+
* The interval is spelled, so recognition reads letters and not only pitch
|
|
22
|
+
* classes: over an Ab bass the augmented sixth is F# and the minor seventh of
|
|
23
|
+
* the bVI7 it otherwise sounds exactly like is Gb. A caller holding pitch
|
|
24
|
+
* classes alone — an analysis of a MIDI track, which names no accidentals —
|
|
25
|
+
* makes that reading with {@link augmentedSixthFromPitchClasses}, and the chord
|
|
26
|
+
* it hands back carries the spelling that decided it.
|
|
17
27
|
*/
|
|
18
28
|
|
|
19
29
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* Supports accidentals (`bVII`, `#iv`), case-based triad quality, the `o`/`ø`/`+`
|
|
23
|
-
* suffixes, sevenths (`V7`, `viio7`, `Imaj7`), figured-bass inversions
|
|
24
|
-
* (`V6`, `V64`, `V65`, `V43`, `V42`), and applied/secondary chords via a slash
|
|
25
|
-
* (`V7/V`, `viio/ii`). The target after the slash is read as a scale degree
|
|
26
|
-
* whose root becomes a local major tonic for the applied chord. Inverted chords
|
|
27
|
-
* carry a `bassPc`.
|
|
30
|
+
* The three augmented-sixth chords, under the nationalities convention gives
|
|
31
|
+
* them.
|
|
28
32
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* {@link chordToRoman} and honor the numeral case: `V9` -> `dom9`, `ii9` ->
|
|
33
|
-
* `min9`, `Imaj9` -> `maj9`, `V11` -> `11`, `V13` -> `13`, `Iadd6`/`iadd6` ->
|
|
34
|
-
* `6`/`min6`, `I69` -> `6/9`, `Isus2`/`Isus4`, `Iadd9`/`Iadd11`, `Ib5` ->
|
|
35
|
-
* `majb5`, `I5` -> `5`, and the altered dominants `V7b9`/`V7#9`/`V7#11`/`V7b13`.
|
|
36
|
-
* A figure string that is neither a recognized inversion nor a supported
|
|
37
|
-
* quality suffix throws rather than silently degrading to a triad.
|
|
33
|
+
* Over an Ab bass in C: the Italian sixth is Ab C F#, the French sixth adds the
|
|
34
|
+
* supertonic (Ab C D F#), and the German sixth adds the lowered mediant
|
|
35
|
+
* (Ab C Eb F#).
|
|
38
36
|
*
|
|
39
|
-
* @
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
* @category Functional Harmony
|
|
38
|
+
*/
|
|
39
|
+
type AugmentedSixthKind = 'italian' | 'french' | 'german';
|
|
40
|
+
/**
|
|
41
|
+
* Build an augmented-sixth chord in a key.
|
|
42
|
+
*
|
|
43
|
+
* The chord always carries an explicit `bassPc` on the lowered submediant,
|
|
44
|
+
* which is what makes it an augmented sixth rather than the dominant seventh or
|
|
45
|
+
* the altered supertonic seventh it shares pitch classes with, and an explicit
|
|
46
|
+
* interval template naming exactly the tones that sound.
|
|
47
|
+
*
|
|
48
|
+
* It also carries its spelling: the tones as `toneSpellings`, and the root and
|
|
49
|
+
* bass as the usual hints. Those are what let the ordinary chord speller and
|
|
50
|
+
* the symbol formatter name the chord — `Ab C Eb F#` and `Ab7` for the German
|
|
51
|
+
* sixth of C major — since a stack of thirds over that root would put a Gb
|
|
52
|
+
* where the augmented sixth belongs. The spelling is the one the key is written
|
|
53
|
+
* on, so a flat-side key takes the double flats it genuinely needs.
|
|
54
|
+
*
|
|
55
|
+
* The lowered submediant is taken as eight semitones above the tonic in every
|
|
56
|
+
* mode, since the chord is a chromatic alteration rather than a degree of the
|
|
57
|
+
* prevailing scale.
|
|
58
|
+
*
|
|
59
|
+
* @param kind Which of the three augmented sixths to build.
|
|
60
|
+
* @param key The prevailing key, as a key name, a key/scale, or a `Key`.
|
|
61
|
+
* @returns The chord, with its bass on the lowered submediant.
|
|
62
|
+
* @throws If `kind` is not one of the three, or the key's root is not finite.
|
|
63
|
+
* @see {@link spellAugmentedSixth} to spell the family from a tonic without
|
|
64
|
+
* building a chord at all.
|
|
42
65
|
* @example
|
|
43
66
|
* ```ts
|
|
44
|
-
* import {
|
|
45
|
-
*
|
|
67
|
+
* import { augmentedSixthChord, majorKey } from '@libraz/libcantus';
|
|
68
|
+
* augmentedSixthChord('german', majorKey(0));
|
|
69
|
+
* // Ab C Eb F# in C: { rootPc: 8, quality: 'dom7', intervals: [0, 4, 7, 10], bassPc: 8, ... }
|
|
46
70
|
* ```
|
|
47
71
|
* @category Functional Harmony
|
|
48
72
|
*/
|
|
49
|
-
declare function
|
|
73
|
+
declare function augmentedSixthChord(kind: AugmentedSixthKind, key: KeyLike): Chord;
|
|
50
74
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
75
|
+
* Identify a chord as one of the augmented sixths, or null when it is none.
|
|
76
|
+
*
|
|
77
|
+
* A chord qualifies when it sounds the lowered submediant of `key` in the bass,
|
|
78
|
+
* its pitch classes are exactly one of the three sets built over that bass, and
|
|
79
|
+
* its own letters spell the augmented sixth above it. All three are needed: the
|
|
80
|
+
* same pitch classes over the same bass are an ordinary bVI7 when the tone ten
|
|
81
|
+
* semitones above the bass is written as a minor seventh, and the letters are
|
|
82
|
+
* the only place that distinction lives.
|
|
83
|
+
*
|
|
84
|
+
* A chord naming no `bassPc` sounds its root lowest, which is how the two
|
|
85
|
+
* conventions for that field — the sounding bass always, or only a bass that
|
|
86
|
+
* differs from the root — are read alike here.
|
|
87
|
+
*
|
|
88
|
+
* The root the chord is measured from is not consulted, so a chord voiced or
|
|
89
|
+
* rebuilt around any of its own tones still identifies as long as it keeps its
|
|
90
|
+
* spelling.
|
|
91
|
+
*
|
|
92
|
+
* @param chord The chord to test, as a chord symbol, chord data, or a `Chord`.
|
|
93
|
+
* @param key The prevailing key, as a key name, a key/scale, or a `Key`.
|
|
94
|
+
* @returns The kind of augmented sixth, or null.
|
|
95
|
+
* @see {@link augmentedSixthFromPitchClasses} to make the reading from pitch
|
|
96
|
+
* classes that carry no spelling at all.
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { augmentedSixthKind, majorKey, romanToChord } from '@libraz/libcantus';
|
|
100
|
+
* augmentedSixthKind(romanToChord('Fr6', majorKey(0)), majorKey(0)); // 'french'
|
|
101
|
+
* ```
|
|
53
102
|
* @category Functional Harmony
|
|
54
103
|
*/
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Render a tonicizing chord as an applied numeral (`V7/V`, `viio7/ii`)
|
|
58
|
-
* instead of naming its root against the home key (`II7`, `#ivo7`).
|
|
59
|
-
*
|
|
60
|
-
* Off by default: naming the root is always a correct spelling, whereas
|
|
61
|
-
* whether a chromatic dominant is genuinely applied is a reading only the
|
|
62
|
-
* caller can make. Turning it on makes {@link chordToRoman} the exact inverse
|
|
63
|
-
* of {@link romanToChord} for the chords {@link secondaryDominant} builds.
|
|
64
|
-
*/
|
|
65
|
-
applied?: boolean;
|
|
66
|
-
};
|
|
104
|
+
declare function augmentedSixthKind(chord: ChordLike, key: KeyLike): AugmentedSixthKind | null;
|
|
67
105
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* @param key The prevailing key.
|
|
89
|
-
* @param opts `applied` renders tonicizing chords as `V7/V`-style numerals.
|
|
90
|
-
* @returns The Roman numeral string.
|
|
106
|
+
* Read a set of sounding pitch classes over a sounding bass as an augmented
|
|
107
|
+
* sixth, or null when they are none.
|
|
108
|
+
*
|
|
109
|
+
* The reading a caller holding no spelling has to make for itself: pitch
|
|
110
|
+
* classes name no accidentals, so nothing downstream can tell the German sixth
|
|
111
|
+
* from the bVI7 it sounds like once the letters are gone. The chord returned is
|
|
112
|
+
* the one {@link augmentedSixthChord} builds, spelling and all, so it keeps
|
|
113
|
+
* identifying as an augmented sixth everywhere it travels.
|
|
114
|
+
*
|
|
115
|
+
* Not every b6-1-b3-#4 over a b6 bass is an augmented sixth, and this cannot
|
|
116
|
+
* know which one is: it answers what the tones would spell, and the caller
|
|
117
|
+
* decides whether that reading is the one the music supports.
|
|
118
|
+
*
|
|
119
|
+
* @param pcs The sounding pitch classes; duplicates and unreduced values are
|
|
120
|
+
* fine.
|
|
121
|
+
* @param bassPc The pitch class sounding in the bass.
|
|
122
|
+
* @param key The prevailing key, as a key name, a key/scale, or a `Key`.
|
|
123
|
+
* @returns The augmented-sixth chord, or null.
|
|
124
|
+
* @see {@link augmentedSixthKind} to read a chord that already carries its
|
|
125
|
+
* spelling.
|
|
91
126
|
* @example
|
|
92
127
|
* ```ts
|
|
93
|
-
* import {
|
|
94
|
-
*
|
|
95
|
-
*
|
|
128
|
+
* import { augmentedSixthFromPitchClasses, majorKey } from '@libraz/libcantus';
|
|
129
|
+
* // Ab C Eb Gb sounding over Ab in C: the German sixth, spelled with its F#.
|
|
130
|
+
* augmentedSixthFromPitchClasses([8, 0, 3, 6], 8, majorKey(0))?.quality; // 'dom7'
|
|
96
131
|
* ```
|
|
97
132
|
* @category Functional Harmony
|
|
98
133
|
*/
|
|
99
|
-
declare function
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* A suspension figure, named by the interval above the bass and the interval it
|
|
103
|
-
* resolves to.
|
|
104
|
-
*
|
|
105
|
-
* @category Arrangement & Analysis
|
|
106
|
-
*/
|
|
107
|
-
type SuspensionFigure = 'sus4-3' | 'sus6-5' | 'sus7-6' | 'sus9-8' | 'sus2-3';
|
|
134
|
+
declare function augmentedSixthFromPitchClasses(pcs: readonly number[], bassPc: number, key: KeyLike): Chord | null;
|
|
108
135
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* @
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
kind: 'suspension';
|
|
127
|
-
type: SuspensionFigure;
|
|
128
|
-
resolveTo: number;
|
|
129
|
-
} | {
|
|
130
|
-
kind: 'anticipation';
|
|
131
|
-
} | {
|
|
132
|
-
kind: 'escape';
|
|
133
|
-
} | {
|
|
134
|
-
kind: 'needsResolution';
|
|
135
|
-
resolveTo: number;
|
|
136
|
-
} | {
|
|
137
|
-
kind: 'leadingTone';
|
|
138
|
-
resolveTo: number;
|
|
139
|
-
};
|
|
140
|
-
/**
|
|
141
|
-
* A note with its theory labels and a short rationale.
|
|
142
|
-
*
|
|
143
|
-
* The labels describe the note against the chord sounding at its **onset**;
|
|
144
|
-
* they are not re-derived for the later chords a long note sustains across.
|
|
145
|
-
* `analyzeArrangement` does re-check every crossed chord change, and reports
|
|
146
|
-
* what it finds as conflicts, so a note labelled a chord tone here can still
|
|
147
|
-
* appear in `conflicts` at a later beat.
|
|
148
|
-
*
|
|
149
|
-
* The identity fields make an annotation mappable back to the caller's input:
|
|
150
|
-
* `originalIndex` is the note's position in the array that was passed in, and
|
|
151
|
-
* `trackIndex` is set by {@link analyzeArrangement}.
|
|
152
|
-
*
|
|
153
|
-
* @category Arrangement & Analysis
|
|
154
|
-
*/
|
|
155
|
-
type AnalyzedNote = {
|
|
156
|
-
noteId: number;
|
|
157
|
-
/** The note's index in the caller's own array, when the caller supplied one. */
|
|
158
|
-
originalIndex?: number;
|
|
159
|
-
/** The note's track, set by `analyzeArrangement`. */
|
|
160
|
-
trackIndex?: number;
|
|
161
|
-
pitch: number;
|
|
162
|
-
startBeat: number;
|
|
163
|
-
durationBeat: number;
|
|
164
|
-
labels: TheoryLabel[];
|
|
165
|
-
rationale?: string;
|
|
166
|
-
};
|
|
167
|
-
/**
|
|
168
|
-
* A single note in a monophonic voice: a {@link NoteEvent} with a stable id and,
|
|
169
|
-
* optionally, its index in the caller's original array.
|
|
170
|
-
*
|
|
171
|
-
* @category Arrangement & Analysis
|
|
172
|
-
*/
|
|
173
|
-
type VoiceNote = NoteEvent & {
|
|
174
|
-
id?: number;
|
|
175
|
-
originalIndex?: number;
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* A voice note whose id has been assigned, as {@link toVoiceNotes} returns and
|
|
179
|
-
* {@link analyzeArrangement} works in.
|
|
180
|
-
*
|
|
181
|
-
* @category Arrangement & Analysis
|
|
182
|
-
*/
|
|
183
|
-
type IdentifiedVoiceNote = VoiceNote & {
|
|
184
|
-
id: number;
|
|
185
|
-
};
|
|
186
|
-
/**
|
|
187
|
-
* Give plain note events the ids {@link analyzeVoice} reports back.
|
|
188
|
-
*
|
|
189
|
-
* The id is only an identity handle, so the array position serves; this exists
|
|
190
|
-
* so the mapping is a named call rather than a `map` every caller writes.
|
|
191
|
-
*
|
|
192
|
-
* @param events The note events, in time order.
|
|
193
|
-
* @returns The same notes as voice notes, each carrying its index as its id.
|
|
194
|
-
* @category Arrangement & Analysis
|
|
195
|
-
*/
|
|
196
|
-
declare function toVoiceNotes(events: readonly NoteEvent[]): IdentifiedVoiceNote[];
|
|
197
|
-
/**
|
|
198
|
-
* Label every note of a voice with its theory roles.
|
|
199
|
-
*
|
|
200
|
-
* Each note is classified against the chord sounding at its beat. Chord tones
|
|
201
|
-
* get a role label; non-chord tones are matched, in order, as suspensions
|
|
202
|
-
* (prepared by an identical consonant pitch and resolving by step), passing
|
|
203
|
-
* tones, neighbors, anticipations, and escape tones, then fall back to tension,
|
|
204
|
-
* avoid, or an unresolved-dissonance label. Leading-tone resolutions are noted
|
|
205
|
-
* additionally.
|
|
206
|
-
*
|
|
207
|
-
* The voice is expected to be monophonic (one note at a time). Notes sharing an
|
|
208
|
-
* onset are treated as simultaneous cluster members, not melodic neighbors, so
|
|
209
|
-
* they receive no melodic labels (suspension, passing, neighbor, anticipation,
|
|
210
|
-
* escape) — only harmonic ones. Callers with truly polyphonic material should
|
|
211
|
-
* split it into monophonic sub-voices first, as `analyzeArrangement` does.
|
|
212
|
-
*
|
|
213
|
-
* @param voice The monophonic voice, in time order. A note without an `id` is
|
|
214
|
-
* identified by its position, so plain note events can be passed straight in.
|
|
215
|
-
* @param chordAtBeat Chord sounding at a given beat, or null.
|
|
216
|
-
* @param key Key context for leading-tone detection.
|
|
217
|
-
* @param otherVoicesAtBeat Other sounding voices at a given beat; defaults to
|
|
218
|
-
* none, which is the whole story for a solo line.
|
|
219
|
-
* @returns One annotation per input note.
|
|
136
|
+
* Spell an augmented-sixth chord from a spelled tonic, lowest note first.
|
|
137
|
+
*
|
|
138
|
+
* This is the spelled counterpart of {@link augmentedSixthChord}, and the table
|
|
139
|
+
* the chord's own spelling is built from: every tone is placed by a named
|
|
140
|
+
* interval above the bass, so the outer interval is an augmented sixth in every
|
|
141
|
+
* key rather than the minor seventh its ten semitones would otherwise imply.
|
|
142
|
+
* Reach for it when a spelled tonic is all there is; spelling the chord itself
|
|
143
|
+
* with {@link spellChord} names the same notes, in the chord's own order.
|
|
144
|
+
*
|
|
145
|
+
* A tonic carrying an octave yields notes carrying octaves, ascending from the
|
|
146
|
+
* bass; an octave-less tonic yields octave-less notes.
|
|
147
|
+
*
|
|
148
|
+
* @param kind Which of the three augmented sixths to spell.
|
|
149
|
+
* @param tonic The spelled tonic of the key, as a note name, a MIDI number,
|
|
150
|
+
* note data, or a `Note`.
|
|
151
|
+
* @returns The spelled tones, bass first and ascending.
|
|
152
|
+
* @throws If `kind` is not one of the three, or `tonic` is not a note.
|
|
220
153
|
* @example
|
|
221
154
|
* ```ts
|
|
222
|
-
* import {
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* { pitch: 62, startBeat: 1, durationBeat: 1 },
|
|
226
|
-
* { pitch: 64, startBeat: 2, durationBeat: 1 },
|
|
227
|
-
* ];
|
|
228
|
-
* const cMajor = makeChord(0, 'maj');
|
|
229
|
-
* const labels = analyzeVoice(voice, () => cMajor, majorKey(0));
|
|
230
|
-
* labels; // one AnalyzedNote per input note, in the same order
|
|
155
|
+
* import { noteNames, parseNote, spellAugmentedSixth } from '@libraz/libcantus';
|
|
156
|
+
* noteNames(spellAugmentedSixth('german', parseNote('C'))); // ['Ab', 'C', 'Eb', 'F#']
|
|
157
|
+
* noteNames(spellAugmentedSixth('italian', 'E')); // ['C', 'E', 'A#']
|
|
231
158
|
* ```
|
|
232
|
-
* @category
|
|
159
|
+
* @category Functional Harmony
|
|
233
160
|
*/
|
|
234
|
-
declare function
|
|
161
|
+
declare function spellAugmentedSixth(kind: AugmentedSixthKind, tonic: NoteLike): Note[];
|
|
235
162
|
|
|
236
163
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
164
|
+
* Line spelling: name every note of a melodic line in one pass.
|
|
165
|
+
*
|
|
166
|
+
* {@link spellPitch} decides one note from the key, the root of the chord under
|
|
167
|
+
* it and its two immediate neighbours. That is enough for a single note, but a
|
|
168
|
+
* line spelled note by note can zig-zag between the two readings of one idea —
|
|
169
|
+
* a rising whole-tone figure comes out F# Ab Bb, with a diminished third in the
|
|
170
|
+
* middle of it.
|
|
171
|
+
*
|
|
172
|
+
* This module solves the whole line at once. The theory layer's own judgment
|
|
173
|
+
* becomes the cost of emitting a spelling, the melodic rules become the cost of
|
|
174
|
+
* moving from one spelling to the next, and a dynamic program takes the
|
|
175
|
+
* cheapest path across the line. Nothing here re-decides what
|
|
176
|
+
* {@link spellPitchClass} and {@link spellChord} already decide: it only weighs
|
|
177
|
+
* their answers against the shape of the line they sit in.
|
|
241
178
|
*/
|
|
242
179
|
|
|
243
180
|
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* Only `drums` changes the analysis: its pitches select instruments rather than
|
|
247
|
-
* naming harmony, so such a track is excluded from chord and key inference and
|
|
248
|
-
* from every voice-leading comparison, and is reported back with no
|
|
249
|
-
* annotations. `harmony` is meaningful through
|
|
250
|
-
* {@link ArrangementOptions.harmonyTracks}, which names the tracks the chords
|
|
251
|
-
* are inferred from. The rest are labels carried through to
|
|
252
|
-
* {@link TrackAnalysis.role} for the caller's own use; nothing is inferred, so
|
|
253
|
-
* a track left unlabelled reports `'other'` rather than a guess.
|
|
254
|
-
*
|
|
255
|
-
* @category Arrangement & Analysis
|
|
256
|
-
*/
|
|
257
|
-
type TrackRole = 'melody' | 'harmony' | 'bass' | 'drums' | 'other';
|
|
258
|
-
/**
|
|
259
|
-
* One input track: its notes plus optional name and role.
|
|
260
|
-
*
|
|
261
|
-
* @category Arrangement & Analysis
|
|
262
|
-
*/
|
|
263
|
-
type ArrangementTrack = {
|
|
264
|
-
name?: string;
|
|
265
|
-
role?: TrackRole;
|
|
266
|
-
notes: readonly NoteEvent[];
|
|
267
|
-
};
|
|
268
|
-
/**
|
|
269
|
-
* A track after analysis: its resolved name, role, and per-note annotations.
|
|
270
|
-
*
|
|
271
|
-
* @category Arrangement & Analysis
|
|
272
|
-
*/
|
|
273
|
-
type TrackAnalysis = {
|
|
274
|
-
name: string;
|
|
275
|
-
role: TrackRole;
|
|
276
|
-
/** One {@link AnalyzedNote} per note, labelled against the inferred chords. */
|
|
277
|
-
notes: AnalyzedNote[];
|
|
278
|
-
};
|
|
279
|
-
/**
|
|
280
|
-
* A note that clashes with the harmony sounding beneath it.
|
|
281
|
-
*
|
|
282
|
-
* @category Arrangement & Analysis
|
|
283
|
-
*/
|
|
284
|
-
type Conflict = {
|
|
285
|
-
beat: number;
|
|
286
|
-
trackName: string;
|
|
287
|
-
/** The clashing track's index in the caller's input array. */
|
|
288
|
-
trackIndex: number;
|
|
289
|
-
/** The note's index in that track's own note array. */
|
|
290
|
-
originalIndex?: number;
|
|
291
|
-
/** The id the note carries in {@link TrackAnalysis.notes}. */
|
|
292
|
-
noteId: number;
|
|
293
|
-
pitch: number;
|
|
294
|
-
/**
|
|
295
|
-
* The note's theory labels at its onset, so a caller can tell a genuine clash
|
|
296
|
-
* from a passing tone, a neighbour, or a prepared suspension — all of which
|
|
297
|
-
* are ordinary non-chord tones that still evaluate as unsafe.
|
|
298
|
-
*/
|
|
299
|
-
labels: TheoryLabel[];
|
|
300
|
-
/** The clash severity: {@link NoteSafety.Warning} or {@link NoteSafety.Dissonant}. */
|
|
301
|
-
safety: NoteSafety;
|
|
302
|
-
/** {@link SafetyResult.reasons} bitmask. */
|
|
303
|
-
reasons: number;
|
|
304
|
-
/** Preferred stepwise resolution, when the safety evaluator provides one. */
|
|
305
|
-
resolveTo?: number;
|
|
306
|
-
/** Nearby fully safe replacement pitches, nearest first. */
|
|
307
|
-
suggestions?: number[];
|
|
308
|
-
rationale?: string;
|
|
309
|
-
};
|
|
310
|
-
/**
|
|
311
|
-
* The full result of {@link analyzeArrangement}.
|
|
181
|
+
* Options for {@link spellLine}.
|
|
312
182
|
*
|
|
313
183
|
* @category Arrangement & Analysis
|
|
314
184
|
*/
|
|
315
|
-
type
|
|
316
|
-
key: KeyScale;
|
|
317
|
-
timeline: ChordTimeline;
|
|
318
|
-
segmentConfidence: number[];
|
|
319
|
-
cadences: CadenceHit[];
|
|
320
|
-
tracks: TrackAnalysis[];
|
|
321
|
-
/** Notes clashing with the sounding harmony, worst severity first. */
|
|
322
|
-
conflicts: Conflict[];
|
|
323
|
-
};
|
|
324
|
-
/**
|
|
325
|
-
* Options controlling {@link analyzeArrangement} and {@link tensionCurve}.
|
|
326
|
-
*
|
|
327
|
-
* @category Arrangement & Analysis
|
|
328
|
-
*/
|
|
329
|
-
type ArrangementOptions = {
|
|
330
|
-
/** Key context; inferred from the pooled notes when omitted. */
|
|
331
|
-
key?: KeyScale;
|
|
332
|
-
/**
|
|
333
|
-
* A chord timeline to analyse against, instead of inferring one from the
|
|
334
|
-
* notes. Supply it to analyse a hand-written progression, or to reuse the
|
|
335
|
-
* timeline of an earlier call rather than re-running chord inference.
|
|
336
|
-
*/
|
|
337
|
-
timeline?: ChordTimeline;
|
|
338
|
-
/**
|
|
339
|
-
* Indices of the tracks the harmony is inferred from. Defaults to every
|
|
340
|
-
* pitched track. Percussion tracks are excluded either way.
|
|
341
|
-
*/
|
|
342
|
-
harmonyTracks?: number[];
|
|
343
|
-
/**
|
|
344
|
-
* Lowest severity reported in `conflicts`.
|
|
345
|
-
*
|
|
346
|
-
* @defaultValue {@link NoteSafety.Warning}
|
|
347
|
-
*/
|
|
348
|
-
minSeverity?: NoteSafety;
|
|
349
|
-
/**
|
|
350
|
-
* Time signature; defaults to 4/4.
|
|
351
|
-
*
|
|
352
|
-
* @defaultValue `4/4`
|
|
353
|
-
*/
|
|
354
|
-
ts?: TimeSignature;
|
|
355
|
-
/**
|
|
356
|
-
* Chord-slot length in beats; defaults to one bar of `ts`.
|
|
357
|
-
*
|
|
358
|
-
* @defaultValue one bar of `ts`
|
|
359
|
-
*/
|
|
360
|
-
harmonicRhythm?: number;
|
|
361
|
-
/**
|
|
362
|
-
* Safety profile used for conflict detection; defaults to `pop`.
|
|
363
|
-
*
|
|
364
|
-
* @defaultValue `'pop'`
|
|
365
|
-
*/
|
|
366
|
-
profile?: SafetyProfile;
|
|
185
|
+
type SpellLineOptions = {
|
|
367
186
|
/**
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* rather than blocking the thread.
|
|
374
|
-
*
|
|
375
|
-
* @defaultValue {@link DEFAULT_GENERATION_BUDGET}
|
|
187
|
+
* Tonic spelling to anchor the key, for a caller that already knows how the
|
|
188
|
+
* piece is written. It must sound the key's own root pitch class; anything
|
|
189
|
+
* else is rejected rather than spelled. Defaults to the conventional
|
|
190
|
+
* spelling `spelledKeyOf` gives the key. Accepted as a note name, a MIDI
|
|
191
|
+
* number, note data, or a `Note`.
|
|
376
192
|
*/
|
|
193
|
+
tonic?: NoteLike;
|
|
194
|
+
/** Upper bound on the work the line may cost; defaults to the generation budget. */
|
|
377
195
|
budget?: number;
|
|
378
196
|
};
|
|
379
197
|
/**
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
* @param
|
|
412
|
-
*
|
|
413
|
-
* @
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* ];
|
|
421
|
-
* const { key, conflicts } = analyzeArrangement([{ role: 'melody', notes: melody }]);
|
|
422
|
-
* conflicts; // notes clashing with the inferred harmony, worst severity first
|
|
423
|
-
* ```
|
|
424
|
-
* @category Arrangement & Analysis
|
|
425
|
-
*/
|
|
426
|
-
declare function analyzeArrangement(tracks: ArrangementTrack[], opts?: ArrangementOptions): ArrangementAnalysis;
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Arrangement tension analysis: a coarse harmonic-tension curve sampled at
|
|
430
|
-
* regular beats across a whole multi-track piece, combining the sounding
|
|
431
|
-
* chord's harmonic function, the dissonance of the sounding notes, and their
|
|
432
|
-
* registral span into a single normalized reading.
|
|
433
|
-
*/
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* A tension reading sampled at a beat.
|
|
437
|
-
*
|
|
438
|
-
* @category Arrangement & Analysis
|
|
439
|
-
*/
|
|
440
|
-
type TensionPoint = {
|
|
441
|
-
beat: number;
|
|
442
|
-
/** Combined tension in [0, 1]. */
|
|
443
|
-
tension: number;
|
|
444
|
-
};
|
|
445
|
-
/**
|
|
446
|
-
* Sample the harmonic tension of an arrangement at regular beats.
|
|
447
|
-
*
|
|
448
|
-
* At each sample beat the tension combines three normalized terms:
|
|
449
|
-
* - the sounding chord's harmonic function (dominant `1`, subdominant `0.5`,
|
|
450
|
-
* tonic or no chord `0`), weighted `FUNCTION_WEIGHT`;
|
|
451
|
-
* - the dissonance of the sounding notes — the larger of the share of sounding
|
|
452
|
-
* pitches that are not chord tones and the share that {@link evaluateSafety}
|
|
453
|
-
* rates {@link NoteSafety.Dissonant} — weighted `DISSONANCE_WEIGHT`;
|
|
454
|
-
* - the registral span of the sounding notes, saturating at
|
|
455
|
-
* `SPAN_SATURATION` semitones, weighted `SPAN_WEIGHT`.
|
|
456
|
-
*
|
|
457
|
-
* The weighted sum is clamped to [0, 1]. The harmony is inferred from all tracks
|
|
458
|
-
* pooled together, so the result is deterministic and self-contained.
|
|
459
|
-
*
|
|
460
|
-
* @param tracks The tracks to sample.
|
|
461
|
-
* @param opts Analysis options plus an optional `step` (default one beat).
|
|
462
|
-
* @returns One {@link TensionPoint} per sampled beat, in beat order.
|
|
463
|
-
* @throws If `step` is not positive.
|
|
464
|
-
* @example
|
|
465
|
-
* ```ts
|
|
466
|
-
* import { tensionCurve } from '@libraz/libcantus';
|
|
467
|
-
* const notes = [{ pitch: 60, startBeat: 0, durationBeat: 4 }];
|
|
468
|
-
* const curve = tensionCurve([{ notes }], { step: 1 });
|
|
469
|
-
* curve; // one { beat, tension } sample per beat, in beat order
|
|
470
|
-
* ```
|
|
471
|
-
* @category Arrangement & Analysis
|
|
472
|
-
*/
|
|
473
|
-
declare function tensionCurve(tracks: ArrangementTrack[], opts?: ArrangementOptions & {
|
|
474
|
-
step?: number;
|
|
475
|
-
}): TensionPoint[];
|
|
476
|
-
/**
|
|
477
|
-
* Sample the tension of an arrangement already analysed by
|
|
478
|
-
* {@link analyzeArrangement}, reusing its harmony instead of inferring it again.
|
|
479
|
-
*
|
|
480
|
-
* `analyzeArrangement` followed by `tensionCurve` runs chord and key inference
|
|
481
|
-
* twice over the same notes. This takes the analysis it already produced, so
|
|
482
|
-
* the curve and the annotations describe the same harmony by construction.
|
|
483
|
-
*
|
|
484
|
-
* @param tracks The same tracks that were analysed.
|
|
485
|
-
* @param analysis The result of {@link analyzeArrangement} for those tracks.
|
|
486
|
-
* @param opts Analysis options plus an optional `step` (default one beat).
|
|
487
|
-
* @returns One {@link TensionPoint} per sampled beat, in beat order.
|
|
198
|
+
* Spell a whole melodic line at once, so its notes agree with each other.
|
|
199
|
+
*
|
|
200
|
+
* Every note is spelled together with the rest of the line rather than one at a
|
|
201
|
+
* time: each pitch offers the three spellings a double accidental allows, the
|
|
202
|
+
* chord and the key price them, the intervals between neighbours price the
|
|
203
|
+
* pairs, and the cheapest reading of the whole line wins. Ties fall to the
|
|
204
|
+
* spelling {@link spellPitchClass} would have chosen alone, so the answer never
|
|
205
|
+
* drifts from the theory layer without a reason.
|
|
206
|
+
*
|
|
207
|
+
* Four rules decide, in this order:
|
|
208
|
+
*
|
|
209
|
+
* 1. A note the sounding chord names takes that chord's spelling of it, even
|
|
210
|
+
* when the key spells the pitch otherwise — the seventh of a Db7 reads Cb in
|
|
211
|
+
* C major, not B.
|
|
212
|
+
* 2. Anything else the key's scale spells keeps its key spelling, exactly as
|
|
213
|
+
* the note-by-note path keeps it.
|
|
214
|
+
* 3. A chromatic note is spelled so the written intervals around it match the
|
|
215
|
+
* sounding ones, and then so a rising step carries a sharp and a falling
|
|
216
|
+
* step a flat.
|
|
217
|
+
* 4. Failing all of that, the key's own chromatic spelling stands, and the
|
|
218
|
+
* fewest accidentals win.
|
|
219
|
+
*
|
|
220
|
+
* Without a timeline — pass `null` — rules 2 to 4 still apply in full, so a
|
|
221
|
+
* diatonic line and a chromatic run come out the same. What is lost is rule 1:
|
|
222
|
+
* a chromatic chord can no longer name its own tones, so the tones of a
|
|
223
|
+
* borrowed or altered chord fall back to the key's convention (that Cb becomes
|
|
224
|
+
* B), and an enharmonically ambiguous tone under a chord that would have fixed
|
|
225
|
+
* it is decided by the line's direction instead.
|
|
226
|
+
*
|
|
227
|
+
* @param notes The line, monophonic and in time order. Notes sharing an onset
|
|
228
|
+
* are read as consecutive, so split polyphonic material into voices first.
|
|
229
|
+
* @param timeline The chords sounding under the line, or null when only the key
|
|
230
|
+
* is known. Each note is judged against the chord at its own onset.
|
|
231
|
+
* @param key The key the line is written in, as a key name, a key/scale, or a
|
|
232
|
+
* `Key`.
|
|
233
|
+
* @param opts Optional tonic spelling and work budget.
|
|
234
|
+
* @returns One spelled note per input note, in input order, each carrying the
|
|
235
|
+
* octave that reproduces its pitch.
|
|
236
|
+
* @throws If `opts.tonic` does not sound the key's root pitch class, if a note
|
|
237
|
+
* event is malformed, or if the line is longer than the budget allows.
|
|
488
238
|
* @example
|
|
489
239
|
* ```ts
|
|
490
|
-
* import {
|
|
491
|
-
*
|
|
492
|
-
* const
|
|
493
|
-
*
|
|
240
|
+
* import { chordTimelineFromChords, majorKey, noteNames, spellLine } from '@libraz/libcantus';
|
|
241
|
+
* // A chromatic ascent spells with sharps, whatever the key would say alone.
|
|
242
|
+
* const rising = [60, 61, 62, 63, 64].map((pitch, index) => ({
|
|
243
|
+
* pitch,
|
|
244
|
+
* startBeat: index,
|
|
245
|
+
* durationBeat: 1,
|
|
246
|
+
* }));
|
|
247
|
+
* noteNames(spellLine(rising, null, majorKey(0)));
|
|
248
|
+
* // ['C4', 'C#4', 'D4', 'D#4', 'E4']
|
|
249
|
+
* // Under a Db7 the same B sounds as the chord's seventh.
|
|
250
|
+
* const timeline = chordTimelineFromChords([{ rootPc: 1, quality: 'dom7', startBeat: 0 }], 4);
|
|
251
|
+
* noteNames(spellLine([{ pitch: 71, startBeat: 0, durationBeat: 1 }], timeline, majorKey(0)));
|
|
252
|
+
* // ['Cb5']
|
|
494
253
|
* ```
|
|
495
254
|
* @category Arrangement & Analysis
|
|
496
255
|
*/
|
|
497
|
-
declare function
|
|
498
|
-
step?: number;
|
|
499
|
-
}): TensionPoint[];
|
|
256
|
+
declare function spellLine(notes: readonly NoteEvent[], timeline: ChordTimeline | null, key: KeyLike, opts?: SpellLineOptions): Note[];
|
|
500
257
|
|
|
501
|
-
export { type
|
|
258
|
+
export { type AugmentedSixthKind, Chord as ChordData, ChordLike, ChordTimeline, KeyLike, Note as NoteData, NoteEvent, NoteLike, type SpellLineOptions, augmentedSixthChord, augmentedSixthFromPitchClasses, augmentedSixthKind, spellAugmentedSixth, spellLine };
|