@libraz/libcantus 0.9.2 → 0.9.4
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 +6 -5
- package/dist/analyze/index.cjs +41 -1676
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +194 -122
- package/dist/analyze/index.d.ts +194 -122
- package/dist/analyze/index.js +19 -8
- package/dist/chunk-36N73KZ3.cjs +246 -0
- package/dist/chunk-36N73KZ3.cjs.map +1 -0
- package/dist/chunk-4HLLZPXT.cjs +3077 -0
- package/dist/chunk-4HLLZPXT.cjs.map +1 -0
- package/dist/chunk-7MGZ7D62.cjs +498 -0
- package/dist/chunk-7MGZ7D62.cjs.map +1 -0
- package/dist/{chunk-R3S2LUBH.js → chunk-7NKDPF74.js} +660 -305
- package/dist/chunk-7NKDPF74.js.map +1 -0
- package/dist/chunk-CAN76NCU.cjs +679 -0
- package/dist/chunk-CAN76NCU.cjs.map +1 -0
- package/dist/{chunk-7Y7WF64D.js → chunk-D2FAJBYL.js} +104 -35
- package/dist/chunk-D2FAJBYL.js.map +1 -0
- package/dist/{chunk-COKTX2G5.js → chunk-EHVEBWM5.js} +1052 -510
- package/dist/chunk-EHVEBWM5.js.map +1 -0
- package/dist/chunk-FCANXBAT.cjs +30 -0
- package/dist/chunk-FCANXBAT.cjs.map +1 -0
- package/dist/chunk-FNDWXRRI.cjs +479 -0
- package/dist/chunk-FNDWXRRI.cjs.map +1 -0
- package/dist/{chunk-GQKN5KDT.js → chunk-GQSGS2N4.js} +43 -29
- package/dist/chunk-GQSGS2N4.js.map +1 -0
- package/dist/chunk-HKGYCQ34.js +53 -0
- package/dist/chunk-HKGYCQ34.js.map +1 -0
- package/dist/chunk-I24MV65U.js +106 -0
- package/dist/chunk-I24MV65U.js.map +1 -0
- package/dist/{chunk-LN7ZZMD2.js → chunk-KMHOICIY.js} +322 -173
- package/dist/chunk-KMHOICIY.js.map +1 -0
- package/dist/chunk-LW53AHQU.cjs +1117 -0
- package/dist/chunk-LW53AHQU.cjs.map +1 -0
- package/dist/chunk-N6IHQUOX.js +1 -0
- package/dist/chunk-N6IHQUOX.js.map +1 -0
- package/dist/chunk-NKIBQ6PT.js +30 -0
- package/dist/chunk-NKIBQ6PT.js.map +1 -0
- package/dist/chunk-NP4NA2FB.cjs +106 -0
- package/dist/chunk-NP4NA2FB.cjs.map +1 -0
- package/dist/chunk-NVE34YGZ.cjs +1449 -0
- package/dist/chunk-NVE34YGZ.cjs.map +1 -0
- package/dist/{chunk-A4532ZFR.js → chunk-NYYWMV2C.js} +115 -47
- package/dist/chunk-NYYWMV2C.js.map +1 -0
- package/dist/{chunk-5HTNMQW4.js → chunk-Q2XUP227.js} +310 -118
- package/dist/chunk-Q2XUP227.js.map +1 -0
- package/dist/chunk-Q6OP4U43.cjs +327 -0
- package/dist/chunk-Q6OP4U43.cjs.map +1 -0
- package/dist/chunk-RC6GBMCD.cjs +845 -0
- package/dist/chunk-RC6GBMCD.cjs.map +1 -0
- package/dist/{chunk-XTC3X6MS.js → chunk-RK35RIN7.js} +453 -152
- package/dist/chunk-RK35RIN7.js.map +1 -0
- package/dist/chunk-RUMU3UA6.cjs +53 -0
- package/dist/chunk-RUMU3UA6.cjs.map +1 -0
- package/dist/chunk-T64C7ZD7.cjs +1 -0
- package/dist/chunk-T64C7ZD7.cjs.map +1 -0
- package/dist/chunk-XEBG3G53.js +498 -0
- package/dist/chunk-XEBG3G53.js.map +1 -0
- package/dist/chunk-YLEMGEFH.js +246 -0
- package/dist/chunk-YLEMGEFH.js.map +1 -0
- package/dist/chunk-ZEJR4TKL.cjs +173 -0
- package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
- package/dist/core/index.cjs +130 -431
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +331 -17
- package/dist/core/index.d.ts +331 -17
- package/dist/core/index.js +71 -13
- package/dist/{function-2bJW1Zp_.d.ts → function-BtiZb9i9.d.ts} +28 -7
- package/dist/{function-r2sh-jOO.d.cts → function-DNugAOrt.d.cts} +28 -7
- package/dist/generate/index.cjs +30 -3751
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +373 -59
- package/dist/generate/index.d.ts +373 -59
- package/dist/generate/index.js +23 -9
- package/dist/{index-DZ1M21do.d.cts → index-6VR8-kit.d.ts} +20 -4
- package/dist/index-70p168VM.d.ts +192 -0
- package/dist/{index-D_ZTUNKp.d.cts → index-Be74mcon.d.cts} +18 -17
- package/dist/index-BtXT6UQ0.d.cts +192 -0
- package/dist/{index-BMPxwjwV.d.ts → index-C5UBbQ4G.d.ts} +18 -17
- package/dist/{index-BOBXBNwP.d.cts → index-C83mZI1P.d.ts} +93 -8
- package/dist/{index-BdeQ1r_p.d.cts → index-D6b6na69.d.cts} +14 -11
- package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
- package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
- package/dist/index-OG65ocOr.d.cts +272 -0
- package/dist/index-OG65ocOr.d.ts +272 -0
- package/dist/{index-BreoJRw0.d.ts → index-tJhkmQgE.d.cts} +20 -4
- package/dist/{index-CP3wAfEH.d.ts → index-tSQh-xwY.d.ts} +14 -11
- package/dist/{index-CM5W4rcm.d.ts → index-y_ak9_Hi.d.cts} +93 -8
- package/dist/index.cjs +419 -6848
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -13
- package/dist/index.d.ts +14 -13
- package/dist/index.js +132 -27
- package/dist/model/index.cjs +8 -2423
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +433 -74
- package/dist/model/index.d.ts +433 -74
- package/dist/model/index.js +6 -7
- package/dist/styled-DIRkk9Uz.d.ts +506 -0
- package/dist/styled-DJbkJQfj.d.cts +506 -0
- package/dist/theory/index.cjs +172 -1676
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +151 -233
- package/dist/theory/index.d.ts +151 -233
- package/dist/theory/index.js +31 -7
- package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
- package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
- package/package.json +73 -24
- package/dist/chunk-5HTNMQW4.js.map +0 -1
- package/dist/chunk-7Y7WF64D.js.map +0 -1
- package/dist/chunk-A4532ZFR.js.map +0 -1
- package/dist/chunk-BHUT2S5G.js +0 -22
- package/dist/chunk-BHUT2S5G.js.map +0 -1
- package/dist/chunk-CCBLVAPK.js +0 -42
- package/dist/chunk-CCBLVAPK.js.map +0 -1
- package/dist/chunk-COKTX2G5.js.map +0 -1
- package/dist/chunk-DYWFNC4A.js +0 -152
- package/dist/chunk-DYWFNC4A.js.map +0 -1
- package/dist/chunk-GQKN5KDT.js.map +0 -1
- package/dist/chunk-LN7ZZMD2.js.map +0 -1
- package/dist/chunk-MSWFZF7J.js +0 -118
- package/dist/chunk-MSWFZF7J.js.map +0 -1
- package/dist/chunk-R3S2LUBH.js.map +0 -1
- package/dist/chunk-SDM3VJJ5.js +0 -41
- package/dist/chunk-SDM3VJJ5.js.map +0 -1
- package/dist/chunk-XTC3X6MS.js.map +0 -1
- package/dist/chunk-ZXW5YIWK.js +0 -55
- package/dist/chunk-ZXW5YIWK.js.map +0 -1
- package/dist/index-CGMZNCx6.d.cts +0 -140
- package/dist/index-CGMZNCx6.d.ts +0 -140
- package/dist/styled-BjXtOFNk.d.cts +0 -282
- package/dist/styled-D7BAx3S3.d.ts +0 -282
- package/src/analyze/arrange/index.ts +0 -21
- package/src/analyze/arrange/internal.ts +0 -125
- package/src/analyze/arrange/tension.ts +0 -187
- package/src/analyze/arrange/tracks.ts +0 -279
- package/src/analyze/detect/index.ts +0 -235
- package/src/analyze/functional/borrowed.ts +0 -84
- package/src/analyze/functional/cadence.ts +0 -66
- package/src/analyze/functional/function.ts +0 -184
- package/src/analyze/functional/index.ts +0 -23
- package/src/analyze/functional/internal.ts +0 -29
- package/src/analyze/functional/roman.ts +0 -393
- package/src/analyze/index.ts +0 -49
- package/src/analyze/timeline/index.ts +0 -415
- package/src/analyze/voice/index.ts +0 -330
- package/src/core/index.ts +0 -47
- package/src/core/interval/index.ts +0 -81
- package/src/core/meter/index.ts +0 -282
- package/src/core/pitch/index.ts +0 -311
- package/src/core/random/index.ts +0 -53
- package/src/core/tuning/index.ts +0 -156
- package/src/core/types.ts +0 -29
- package/src/generate/bass/index.ts +0 -350
- package/src/generate/countermelody/index.ts +0 -379
- package/src/generate/drums/beat.ts +0 -266
- package/src/generate/drums/euclid.ts +0 -58
- package/src/generate/drums/fills.ts +0 -356
- package/src/generate/drums/ghost.ts +0 -119
- package/src/generate/drums/hihat.ts +0 -285
- package/src/generate/drums/hit.ts +0 -31
- package/src/generate/drums/index.ts +0 -329
- package/src/generate/drums/internal.ts +0 -257
- package/src/generate/drums/kick.ts +0 -160
- package/src/generate/drums/percussion.ts +0 -121
- package/src/generate/drums/rng.ts +0 -7
- package/src/generate/drums/swing.ts +0 -71
- package/src/generate/groove/index.ts +0 -326
- package/src/generate/harmonize/index.ts +0 -440
- package/src/generate/index.ts +0 -59
- package/src/generate/motif/index.ts +0 -398
- package/src/generate/progression/index.ts +0 -370
- package/src/generate/reharmony/index.ts +0 -330
- package/src/generate/rhythm/index.ts +0 -165
- package/src/index.ts +0 -247
- package/src/model/chord.ts +0 -423
- package/src/model/index.ts +0 -15
- package/src/model/interval.ts +0 -82
- package/src/model/key.ts +0 -266
- package/src/model/note.ts +0 -176
- package/src/model/progression.ts +0 -161
- package/src/model/shared.ts +0 -12
- package/src/theory/chord/index.ts +0 -373
- package/src/theory/chordscale/index.ts +0 -374
- package/src/theory/counterpoint/index.ts +0 -230
- package/src/theory/harmony/index.ts +0 -92
- package/src/theory/index.ts +0 -107
- package/src/theory/safety/index.ts +0 -419
- package/src/theory/scale/degrees.ts +0 -123
- package/src/theory/scale/index.ts +0 -28
- package/src/theory/scale/key.ts +0 -56
- package/src/theory/scale/masks.ts +0 -153
- package/src/theory/spelling/index.ts +0 -182
- package/src/theory/symbol/index.ts +0 -268
- package/src/theory/voicing/index.ts +0 -5
- package/src/theory/voicing/internal.ts +0 -168
- package/src/theory/voicing/leading.ts +0 -112
- package/src/theory/voicing/satb.ts +0 -195
- package/src/theory/voicing/styled.ts +0 -176
- package/tsconfig.json +0 -23
package/dist/analyze/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { T as TimeSignature } from '../index-
|
|
2
|
-
import { K as KeyScale, N as NoteEvent } from '../types-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
export {
|
|
1
|
+
import { T as TimeSignature } from '../index-DXd9JikN.js';
|
|
2
|
+
import { K as KeyScale, N as NoteEvent } from '../types-BN6_Q9Kl.js';
|
|
3
|
+
import { C as Chord, d as ChordToneRole } from '../index-C83mZI1P.js';
|
|
4
|
+
export { a as ChordQuality, b as ChordSegment, c as ChordSpan, P as PitchSpelling } from '../index-C83mZI1P.js';
|
|
5
|
+
import { V as VoiceSnapshot, N as NoteSafety, S as SafetyProfile } from '../index-6VR8-kit.js';
|
|
6
|
+
import { a as ChordTimeline, C as CadenceHit } from '../index-C5UBbQ4G.js';
|
|
7
|
+
export { b as ChordTimelineOptions, c as ChordTimelineResult, d as chordTimelineFromChords, e as chordTimelineFromNotes, f as detectCadences } from '../index-C5UBbQ4G.js';
|
|
8
|
+
export { C as ChordMatch, D as DetectChordOptions, a as DetectKeyOptions, K as KeyMatch, b as KeyVariant, d as detectChord, c as detectChordBest, e as detectKey, f as detectKeyBest, g as detectKeyFromNotes } from '../index-70p168VM.js';
|
|
9
|
+
export { B as BorrowedSource, C as Cadence, a as ChordAnalysis, H as HarmonicFunction, b as analyzeChord, c as borrowedSource, d as detectCadence, f as functionOf, i as isBorrowedChord, e as isDiatonic, g as isMinorKey, p as parallelKey, s as secondaryDominant } from '../function-BtiZb9i9.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Roman-numeral parsing and formatting.
|
|
@@ -45,30 +47,64 @@ export { B as BorrowedSource, C as Cadence, a as ChordAnalysis, H as HarmonicFun
|
|
|
45
47
|
* @category Functional Harmony
|
|
46
48
|
*/
|
|
47
49
|
declare function romanToChord(text: string, key: KeyScale): Chord;
|
|
50
|
+
/**
|
|
51
|
+
* Options for {@link chordToRoman}.
|
|
52
|
+
*
|
|
53
|
+
* @category Functional Harmony
|
|
54
|
+
*/
|
|
55
|
+
type ChordToRomanOptions = {
|
|
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
|
+
};
|
|
48
67
|
/**
|
|
49
68
|
* Render a chord as a Roman numeral relative to a key.
|
|
50
69
|
*
|
|
51
70
|
* Diatonic roots take their scale-degree numeral directly, so numerals are
|
|
52
71
|
* correct in both major and minor keys (and any custom scale). The quality
|
|
53
72
|
* selects the case and suffix; chromatic roots receive a flat/sharp spelling by
|
|
54
|
-
* convention.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
73
|
+
* convention. A key that is not heptatonic has no numeral of its own for every
|
|
74
|
+
* degree, so its roots are named against its parallel major.
|
|
75
|
+
*
|
|
76
|
+
* When the chord carries a `bassPc` on a chord tone, a figured-bass inversion
|
|
77
|
+
* (`6`, `64`, `65`, `43`, `42`) is emitted for plain triads and true seventh
|
|
78
|
+
* chords; added-tone and extended qualities have no lossless figure and render
|
|
79
|
+
* in root position instead (the bass is dropped, pitch classes are preserved).
|
|
80
|
+
*
|
|
81
|
+
* A `bassPc` that is *not* a chord tone — a pedal or passing bass such as
|
|
82
|
+
* `C/D` — is likewise dropped, and the numeral names the upper chord alone.
|
|
83
|
+
* Roman-numeral notation spends its slash on applied chords (`V7/V`), so there
|
|
84
|
+
* is no unambiguous way to write such a bass; a caller that must keep it should
|
|
85
|
+
* read `chord.bassPc` alongside the numeral.
|
|
59
86
|
*
|
|
60
87
|
* @param chord The chord to name.
|
|
61
88
|
* @param key The prevailing key.
|
|
89
|
+
* @param opts `applied` renders tonicizing chords as `V7/V`-style numerals.
|
|
62
90
|
* @returns The Roman numeral string.
|
|
63
91
|
* @example
|
|
64
92
|
* ```ts
|
|
65
93
|
* import { chordToRoman, makeChord, majorKey } from '@libraz/libcantus';
|
|
66
94
|
* chordToRoman(makeChord(7, 'dom7'), majorKey(0)); // => 'V7' (G7 in C major)
|
|
95
|
+
* chordToRoman(makeChord(2, 'dom7'), majorKey(0), { applied: true }); // => 'V7/V'
|
|
67
96
|
* ```
|
|
68
97
|
* @category Functional Harmony
|
|
69
98
|
*/
|
|
70
|
-
declare function chordToRoman(chord: Chord, key: KeyScale): string;
|
|
99
|
+
declare function chordToRoman(chord: Chord, key: KeyScale, opts?: ChordToRomanOptions): string;
|
|
71
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';
|
|
72
108
|
/**
|
|
73
109
|
* A theory annotation attached to a note.
|
|
74
110
|
*
|
|
@@ -76,7 +112,7 @@ declare function chordToRoman(chord: Chord, key: KeyScale): string;
|
|
|
76
112
|
*/
|
|
77
113
|
type TheoryLabel = {
|
|
78
114
|
kind: 'chordTone';
|
|
79
|
-
role:
|
|
115
|
+
role: ChordToneRole;
|
|
80
116
|
} | {
|
|
81
117
|
kind: 'tension';
|
|
82
118
|
degree: 9 | 11 | 13;
|
|
@@ -88,7 +124,7 @@ type TheoryLabel = {
|
|
|
88
124
|
kind: 'neighbor';
|
|
89
125
|
} | {
|
|
90
126
|
kind: 'suspension';
|
|
91
|
-
type:
|
|
127
|
+
type: SuspensionFigure;
|
|
92
128
|
resolveTo: number;
|
|
93
129
|
} | {
|
|
94
130
|
kind: 'anticipation';
|
|
@@ -104,21 +140,60 @@ type TheoryLabel = {
|
|
|
104
140
|
/**
|
|
105
141
|
* A note with its theory labels and a short rationale.
|
|
106
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
|
+
*
|
|
107
153
|
* @category Arrangement & Analysis
|
|
108
154
|
*/
|
|
109
155
|
type AnalyzedNote = {
|
|
110
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;
|
|
111
164
|
labels: TheoryLabel[];
|
|
112
165
|
rationale?: string;
|
|
113
166
|
};
|
|
114
167
|
/**
|
|
115
|
-
* A single note in a monophonic voice: a {@link NoteEvent} with a stable id
|
|
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.
|
|
116
170
|
*
|
|
117
171
|
* @category Arrangement & Analysis
|
|
118
172
|
*/
|
|
119
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 & {
|
|
120
184
|
id: number;
|
|
121
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[];
|
|
122
197
|
/**
|
|
123
198
|
* Label every note of a voice with its theory roles.
|
|
124
199
|
*
|
|
@@ -135,26 +210,28 @@ type VoiceNote = NoteEvent & {
|
|
|
135
210
|
* escape) — only harmonic ones. Callers with truly polyphonic material should
|
|
136
211
|
* split it into monophonic sub-voices first, as `analyzeArrangement` does.
|
|
137
212
|
*
|
|
138
|
-
* @param voice The monophonic voice, in time order.
|
|
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.
|
|
139
215
|
* @param chordAtBeat Chord sounding at a given beat, or null.
|
|
140
216
|
* @param key Key context for leading-tone detection.
|
|
141
|
-
* @param otherVoicesAtBeat Other sounding voices at a given beat
|
|
217
|
+
* @param otherVoicesAtBeat Other sounding voices at a given beat; defaults to
|
|
218
|
+
* none, which is the whole story for a solo line.
|
|
142
219
|
* @returns One annotation per input note.
|
|
143
220
|
* @example
|
|
144
221
|
* ```ts
|
|
145
222
|
* import { analyzeVoice, makeChord, majorKey } from '@libraz/libcantus';
|
|
146
223
|
* const voice = [
|
|
147
|
-
* {
|
|
148
|
-
* {
|
|
149
|
-
* {
|
|
224
|
+
* { pitch: 60, startBeat: 0, durationBeat: 1 },
|
|
225
|
+
* { pitch: 62, startBeat: 1, durationBeat: 1 },
|
|
226
|
+
* { pitch: 64, startBeat: 2, durationBeat: 1 },
|
|
150
227
|
* ];
|
|
151
228
|
* const cMajor = makeChord(0, 'maj');
|
|
152
|
-
* const labels = analyzeVoice(voice, () => cMajor, majorKey(0)
|
|
229
|
+
* const labels = analyzeVoice(voice, () => cMajor, majorKey(0));
|
|
153
230
|
* labels; // one AnalyzedNote per input note, in the same order
|
|
154
231
|
* ```
|
|
155
232
|
* @category Arrangement & Analysis
|
|
156
233
|
*/
|
|
157
|
-
declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) => Chord | null, key: KeyScale, otherVoicesAtBeat
|
|
234
|
+
declare function analyzeVoice(voice: readonly VoiceNote[], chordAtBeat: (beat: number) => Chord | null, key: KeyScale, otherVoicesAtBeat?: (beat: number) => VoiceSnapshot[]): AnalyzedNote[];
|
|
158
235
|
|
|
159
236
|
/**
|
|
160
237
|
* Arrangement track-role analysis: a single harmony is inferred from every
|
|
@@ -166,9 +243,18 @@ declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) =>
|
|
|
166
243
|
/**
|
|
167
244
|
* The musical role a track plays in the arrangement.
|
|
168
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
|
+
*
|
|
169
255
|
* @category Arrangement & Analysis
|
|
170
256
|
*/
|
|
171
|
-
type TrackRole = 'melody' | 'harmony' | 'bass' | 'other';
|
|
257
|
+
type TrackRole = 'melody' | 'harmony' | 'bass' | 'drums' | 'other';
|
|
172
258
|
/**
|
|
173
259
|
* One input track: its notes plus optional name and role.
|
|
174
260
|
*
|
|
@@ -177,7 +263,7 @@ type TrackRole = 'melody' | 'harmony' | 'bass' | 'other';
|
|
|
177
263
|
type ArrangementTrack = {
|
|
178
264
|
name?: string;
|
|
179
265
|
role?: TrackRole;
|
|
180
|
-
notes: NoteEvent[];
|
|
266
|
+
notes: readonly NoteEvent[];
|
|
181
267
|
};
|
|
182
268
|
/**
|
|
183
269
|
* A track after analysis: its resolved name, role, and per-note annotations.
|
|
@@ -198,11 +284,27 @@ type TrackAnalysis = {
|
|
|
198
284
|
type Conflict = {
|
|
199
285
|
beat: number;
|
|
200
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;
|
|
201
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[];
|
|
202
300
|
/** The clash severity: {@link NoteSafety.Warning} or {@link NoteSafety.Dissonant}. */
|
|
203
301
|
safety: NoteSafety;
|
|
204
|
-
/** {@link
|
|
302
|
+
/** {@link SafetyResult.reasons} bitmask. */
|
|
205
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[];
|
|
206
308
|
rationale?: string;
|
|
207
309
|
};
|
|
208
310
|
/**
|
|
@@ -227,6 +329,23 @@ type ArrangementAnalysis = {
|
|
|
227
329
|
type ArrangementOptions = {
|
|
228
330
|
/** Key context; inferred from the pooled notes when omitted. */
|
|
229
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;
|
|
230
349
|
/**
|
|
231
350
|
* Time signature; defaults to 4/4.
|
|
232
351
|
*
|
|
@@ -245,16 +364,31 @@ type ArrangementOptions = {
|
|
|
245
364
|
* @defaultValue `'pop'`
|
|
246
365
|
*/
|
|
247
366
|
profile?: SafetyProfile;
|
|
367
|
+
/**
|
|
368
|
+
* Upper bound on the work this call may do — note counts, windows, and
|
|
369
|
+
* candidate counts are each checked against it before anything is allocated.
|
|
370
|
+
*
|
|
371
|
+
* Raise it to analyse a piece larger than the default allows; the default is
|
|
372
|
+
* {@link DEFAULT_GENERATION_BUDGET}, chosen so a runaway input fails fast
|
|
373
|
+
* rather than blocking the thread.
|
|
374
|
+
*
|
|
375
|
+
* @defaultValue {@link DEFAULT_GENERATION_BUDGET}
|
|
376
|
+
*/
|
|
377
|
+
budget?: number;
|
|
248
378
|
};
|
|
249
379
|
/**
|
|
250
380
|
* Analyse a whole arrangement against a single inferred harmony.
|
|
251
381
|
*
|
|
252
|
-
* The chord timeline and key are inferred from the pooled notes of every
|
|
253
|
-
* (see {@link chordTimelineFromNotes}); pooling all voices is
|
|
254
|
-
* roles are absent or a track doubles the harmony, so it is
|
|
255
|
-
* deriving the harmony from a subset.
|
|
256
|
-
*
|
|
257
|
-
*
|
|
382
|
+
* The chord timeline and key are inferred from the pooled notes of every
|
|
383
|
+
* pitched track (see {@link chordTimelineFromNotes}); pooling all voices is
|
|
384
|
+
* robust even when roles are absent or a track doubles the harmony, so it is
|
|
385
|
+
* preferred over deriving the harmony from a subset. A track marked
|
|
386
|
+
* `role: 'drums'` is excluded — its pitches name instruments, not harmony, and
|
|
387
|
+
* pooling them would corrupt the key, the timeline and every reading built on
|
|
388
|
+
* them. Pass `harmonyTracks` to restrict inference further, or `timeline` to
|
|
389
|
+
* analyse against a progression you already have. Zero- and negative-length
|
|
390
|
+
* notes never sound, so they are dropped at ingest and appear in neither the
|
|
391
|
+
* annotations nor the conflicts.
|
|
258
392
|
*
|
|
259
393
|
* Voice analysis assumes one monophonic voice at a time, so each track is
|
|
260
394
|
* first partitioned into monophonic sub-voices (a polyphonic block-chord track
|
|
@@ -267,6 +401,13 @@ type ArrangementOptions = {
|
|
|
267
401
|
* evaluations that are not {@link NoteSafety.Safe} become conflicts, one per
|
|
268
402
|
* clashing beat, sorted worst severity first and then by beat.
|
|
269
403
|
*
|
|
404
|
+
* Every ordinary non-chord tone — a passing note, a neighbour, a prepared
|
|
405
|
+
* suspension — is by definition unsafe against the chord under it, so the
|
|
406
|
+
* conflict list is a report, not a fault list. Each conflict carries the note's
|
|
407
|
+
* `labels` and its `trackIndex`/`originalIndex`, so a caller can tell those
|
|
408
|
+
* apart and map a conflict back to the note it passed in; `minSeverity` narrows
|
|
409
|
+
* the list to outright dissonance.
|
|
410
|
+
*
|
|
270
411
|
* @param tracks The tracks to analyse.
|
|
271
412
|
* @param opts Analysis options; see {@link ArrangementOptions}.
|
|
272
413
|
* @returns The inferred harmony, per-track annotations, cadences, and conflicts.
|
|
@@ -306,12 +447,12 @@ type TensionPoint = {
|
|
|
306
447
|
*
|
|
307
448
|
* At each sample beat the tension combines three normalized terms:
|
|
308
449
|
* - the sounding chord's harmonic function (dominant `1`, subdominant `0.5`,
|
|
309
|
-
* tonic or no chord `0`), weighted
|
|
450
|
+
* tonic or no chord `0`), weighted `FUNCTION_WEIGHT`;
|
|
310
451
|
* - the dissonance of the sounding notes — the larger of the share of sounding
|
|
311
452
|
* pitches that are not chord tones and the share that {@link evaluateSafety}
|
|
312
|
-
* rates {@link NoteSafety.Dissonant} — weighted
|
|
453
|
+
* rates {@link NoteSafety.Dissonant} — weighted `DISSONANCE_WEIGHT`;
|
|
313
454
|
* - the registral span of the sounding notes, saturating at
|
|
314
|
-
*
|
|
455
|
+
* `SPAN_SATURATION` semitones, weighted `SPAN_WEIGHT`.
|
|
315
456
|
*
|
|
316
457
|
* The weighted sum is clamped to [0, 1]. The harmony is inferred from all tracks
|
|
317
458
|
* pooled together, so the result is deterministic and self-contained.
|
|
@@ -332,98 +473,29 @@ type TensionPoint = {
|
|
|
332
473
|
declare function tensionCurve(tracks: ArrangementTrack[], opts?: ArrangementOptions & {
|
|
333
474
|
step?: number;
|
|
334
475
|
}): TensionPoint[];
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Recognition: infer a chord name from a set of pitches, or a key from a set of
|
|
338
|
-
* pitch classes. This is the inverse direction of the chord/scale builders,
|
|
339
|
-
* which only go name -> notes.
|
|
340
|
-
*/
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* A candidate chord interpretation of a pitch set.
|
|
344
|
-
*
|
|
345
|
-
* @category Recognition
|
|
346
|
-
*/
|
|
347
|
-
type ChordMatch = {
|
|
348
|
-
rootPc: number;
|
|
349
|
-
quality: ChordQuality;
|
|
350
|
-
/** Chord tones absent from the input (an incomplete voicing). */
|
|
351
|
-
missingPcs: number[];
|
|
352
|
-
/** Input pitch classes not belonging to the chord. */
|
|
353
|
-
extraPcs: number[];
|
|
354
|
-
/** True when the input pitch-class set equals the chord exactly. */
|
|
355
|
-
exact: boolean;
|
|
356
|
-
/** Inversion implied by the lowest note: 0 root position, 1 first, ... */
|
|
357
|
-
inversion: number;
|
|
358
|
-
/** Bass pitch class when inverted (the lowest note is not the root). */
|
|
359
|
-
bassPc?: number;
|
|
360
|
-
};
|
|
361
|
-
/**
|
|
362
|
-
* A candidate key interpretation of a pitch-class set.
|
|
363
|
-
*
|
|
364
|
-
* @category Recognition
|
|
365
|
-
*/
|
|
366
|
-
type KeyMatch = {
|
|
367
|
-
key: KeyScale;
|
|
368
|
-
mode: 'major' | 'minor';
|
|
369
|
-
/** Fraction of input pitch classes that are in the scale, in [0, 1]. */
|
|
370
|
-
fit: number;
|
|
371
|
-
};
|
|
372
476
|
/**
|
|
373
|
-
*
|
|
477
|
+
* Sample the tension of an arrangement already analysed by
|
|
478
|
+
* {@link analyzeArrangement}, reusing its harmony instead of inferring it again.
|
|
374
479
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
* voicings); matches are ranked best-first by fewest extra notes, then fewest
|
|
379
|
-
* missing notes, then most specific (largest) chord. An exact match (no extras,
|
|
380
|
-
* no missing) is flagged and ranked first.
|
|
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.
|
|
381
483
|
*
|
|
382
|
-
* @param
|
|
383
|
-
* @
|
|
384
|
-
* @
|
|
385
|
-
*
|
|
386
|
-
* import { detectChord } from '@libraz/libcantus';
|
|
387
|
-
* const matches = detectChord([60, 64, 67]); // C E G
|
|
388
|
-
* matches[0]; // { rootPc: 0, quality: 'maj', exact: true, ... }
|
|
389
|
-
* ```
|
|
390
|
-
* @category Recognition
|
|
391
|
-
*/
|
|
392
|
-
declare function detectChord(pitches: number[]): ChordMatch[];
|
|
393
|
-
/**
|
|
394
|
-
* The single best chord interpretation of a pitch set, or null if none.
|
|
395
|
-
*
|
|
396
|
-
* @param pitches MIDI pitches or bare pitch classes.
|
|
397
|
-
* @returns The top-ranked chord, or null when nothing matches.
|
|
398
|
-
* @example
|
|
399
|
-
* ```ts
|
|
400
|
-
* import { detectChordBest } from '@libraz/libcantus';
|
|
401
|
-
* detectChordBest([60, 64, 67]); // C major triad: { rootPc: 0, quality: 'maj', ... }
|
|
402
|
-
* ```
|
|
403
|
-
* @category Recognition
|
|
404
|
-
*/
|
|
405
|
-
declare function detectChordBest(pitches: number[]): Chord | null;
|
|
406
|
-
/**
|
|
407
|
-
* Rank major and minor keys by how well they contain a set of pitch classes.
|
|
408
|
-
*
|
|
409
|
-
* The tonic is weighted so that, among equally-fitting keys, the one whose root
|
|
410
|
-
* appears in the input is preferred. Minor candidates are scored against the
|
|
411
|
-
* natural, harmonic, and melodic minor variants and keep the best of the three,
|
|
412
|
-
* so a minor cadence containing the leading tone still resolves to its own
|
|
413
|
-
* tonic; the returned key is always the natural-minor scale. Returns all 24
|
|
414
|
-
* keys ranked best-first, or an empty array for an empty input (mirroring
|
|
415
|
-
* {@link detectChord}).
|
|
416
|
-
*
|
|
417
|
-
* @param pitches MIDI pitches or bare pitch classes.
|
|
418
|
-
* @returns Ranked key interpretations (empty for an empty input).
|
|
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.
|
|
419
488
|
* @example
|
|
420
489
|
* ```ts
|
|
421
|
-
* import {
|
|
422
|
-
* const
|
|
423
|
-
*
|
|
490
|
+
* import { analyzeArrangement, tensionCurveFrom } from '@libraz/libcantus';
|
|
491
|
+
* const tracks = [{ notes: [{ pitch: 60, startBeat: 0, durationBeat: 4 }] }];
|
|
492
|
+
* const analysis = analyzeArrangement(tracks);
|
|
493
|
+
* tensionCurveFrom(tracks, analysis, { step: 1 });
|
|
424
494
|
* ```
|
|
425
|
-
* @category
|
|
495
|
+
* @category Arrangement & Analysis
|
|
426
496
|
*/
|
|
427
|
-
declare function
|
|
497
|
+
declare function tensionCurveFrom(tracks: ArrangementTrack[], analysis: ArrangementAnalysis, opts?: ArrangementOptions & {
|
|
498
|
+
step?: number;
|
|
499
|
+
}): TensionPoint[];
|
|
428
500
|
|
|
429
|
-
export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, CadenceHit,
|
|
501
|
+
export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, CadenceHit, Chord as ChordData, ChordTimeline, type ChordToRomanOptions, ChordToneRole, type Conflict, type IdentifiedVoiceNote, KeyScale, NoteEvent, NoteSafety, SafetyProfile, type SuspensionFigure, type TensionPoint, type TheoryLabel, TimeSignature, type TrackAnalysis, type TrackRole, type VoiceNote, VoiceSnapshot, analyzeArrangement, analyzeVoice, chordToRoman, romanToChord, tensionCurve, tensionCurveFrom, toVoiceNotes };
|
package/dist/analyze/index.js
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
chordTimelineFromChords,
|
|
5
5
|
chordTimelineFromNotes,
|
|
6
6
|
detectCadences,
|
|
7
|
-
tensionCurve
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
tensionCurve,
|
|
8
|
+
tensionCurveFrom,
|
|
9
|
+
toVoiceNotes
|
|
10
|
+
} from "../chunk-Q2XUP227.js";
|
|
11
|
+
import "../chunk-YLEMGEFH.js";
|
|
10
12
|
import {
|
|
11
13
|
analyzeChord,
|
|
12
14
|
borrowedSource,
|
|
@@ -15,6 +17,8 @@ import {
|
|
|
15
17
|
detectChord,
|
|
16
18
|
detectChordBest,
|
|
17
19
|
detectKey,
|
|
20
|
+
detectKeyBest,
|
|
21
|
+
detectKeyFromNotes,
|
|
18
22
|
functionOf,
|
|
19
23
|
isBorrowedChord,
|
|
20
24
|
isDiatonic,
|
|
@@ -22,11 +26,14 @@ import {
|
|
|
22
26
|
parallelKey,
|
|
23
27
|
romanToChord,
|
|
24
28
|
secondaryDominant
|
|
25
|
-
} from "../chunk-
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
} from "../chunk-KMHOICIY.js";
|
|
30
|
+
import {
|
|
31
|
+
NoteSafety
|
|
32
|
+
} from "../chunk-NYYWMV2C.js";
|
|
33
|
+
import "../chunk-D2FAJBYL.js";
|
|
34
|
+
import "../chunk-XEBG3G53.js";
|
|
29
35
|
export {
|
|
36
|
+
NoteSafety,
|
|
30
37
|
analyzeArrangement,
|
|
31
38
|
analyzeChord,
|
|
32
39
|
analyzeVoice,
|
|
@@ -39,6 +46,8 @@ export {
|
|
|
39
46
|
detectChord,
|
|
40
47
|
detectChordBest,
|
|
41
48
|
detectKey,
|
|
49
|
+
detectKeyBest,
|
|
50
|
+
detectKeyFromNotes,
|
|
42
51
|
functionOf,
|
|
43
52
|
isBorrowedChord,
|
|
44
53
|
isDiatonic,
|
|
@@ -46,6 +55,8 @@ export {
|
|
|
46
55
|
parallelKey,
|
|
47
56
|
romanToChord,
|
|
48
57
|
secondaryDominant,
|
|
49
|
-
tensionCurve
|
|
58
|
+
tensionCurve,
|
|
59
|
+
tensionCurveFrom,
|
|
60
|
+
toVoiceNotes
|
|
50
61
|
};
|
|
51
62
|
//# sourceMappingURL=index.js.map
|