@libraz/libcantus 0.9.1 → 0.9.3
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 +19 -3
- package/dist/analyze/index.cjs +1970 -0
- package/dist/analyze/index.cjs.map +1 -0
- package/dist/analyze/index.d.cts +341 -0
- package/dist/analyze/index.d.ts +341 -0
- package/dist/analyze/index.js +51 -0
- package/dist/analyze/index.js.map +1 -0
- package/dist/chunk-2ZWMSJH6.js +30 -0
- package/dist/chunk-2ZWMSJH6.js.map +1 -0
- package/dist/chunk-4MHA432G.js +2854 -0
- package/dist/chunk-4MHA432G.js.map +1 -0
- package/dist/chunk-62QZKCJQ.js +49 -0
- package/dist/chunk-62QZKCJQ.js.map +1 -0
- package/dist/chunk-AKLYPWIN.js +83 -0
- package/dist/chunk-AKLYPWIN.js.map +1 -0
- package/dist/chunk-C5XYKUVG.js +904 -0
- package/dist/chunk-C5XYKUVG.js.map +1 -0
- package/dist/chunk-CHMXELDS.js +549 -0
- package/dist/chunk-CHMXELDS.js.map +1 -0
- package/dist/chunk-DYWFNC4A.js +152 -0
- package/dist/chunk-DYWFNC4A.js.map +1 -0
- package/dist/chunk-EH6EM37U.js +315 -0
- package/dist/chunk-EH6EM37U.js.map +1 -0
- package/dist/chunk-FBQK3ZQN.js +131 -0
- package/dist/chunk-FBQK3ZQN.js.map +1 -0
- package/dist/chunk-GOAW4VYL.js +719 -0
- package/dist/chunk-GOAW4VYL.js.map +1 -0
- package/dist/chunk-I5R7ZBJS.js +202 -0
- package/dist/chunk-I5R7ZBJS.js.map +1 -0
- package/dist/chunk-IT2O4XFE.js +154 -0
- package/dist/chunk-IT2O4XFE.js.map +1 -0
- package/dist/chunk-MIWO425M.js +412 -0
- package/dist/chunk-MIWO425M.js.map +1 -0
- package/dist/chunk-N6IHQUOX.js +1 -0
- package/dist/chunk-N6IHQUOX.js.map +1 -0
- package/dist/chunk-SGU7SPLK.js +924 -0
- package/dist/chunk-SGU7SPLK.js.map +1 -0
- package/dist/core/index.cjs +648 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +262 -0
- package/dist/core/index.d.ts +262 -0
- package/dist/core/index.js +93 -0
- package/dist/core/index.js.map +1 -0
- package/dist/function-DVH4Eusr.d.ts +187 -0
- package/dist/function-DYt4iKsn.d.cts +187 -0
- package/dist/generate/index.cjs +4123 -0
- package/dist/generate/index.cjs.map +1 -0
- package/dist/generate/index.d.cts +986 -0
- package/dist/generate/index.d.ts +986 -0
- package/dist/generate/index.js +52 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/index-BNlGP86A.d.cts +157 -0
- package/dist/index-Bg5dYMmZ.d.ts +56 -0
- package/dist/index-Bt4tOUD9.d.ts +157 -0
- package/dist/index-CGMZNCx6.d.cts +140 -0
- package/dist/index-CGMZNCx6.d.ts +140 -0
- package/dist/index-Cl7mhOFM.d.cts +164 -0
- package/dist/index-Cl7mhOFM.d.ts +164 -0
- package/dist/index-CnZCo29u.d.cts +108 -0
- package/dist/index-CpOsP1y9.d.cts +151 -0
- package/dist/index-CxqBQtH1.d.cts +128 -0
- package/dist/index-Dikst44v.d.cts +56 -0
- package/dist/index-DxvARIP2.d.ts +151 -0
- package/dist/index-Jc0Ryi79.d.ts +128 -0
- package/dist/index-JnF4v9bx.d.ts +108 -0
- package/dist/index.cjs +3710 -3143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -3982
- package/dist/index.d.ts +15 -3982
- package/dist/index.js +191 -6504
- package/dist/index.js.map +1 -1
- package/dist/model/index.cjs +2574 -0
- package/dist/model/index.cjs.map +1 -0
- package/dist/model/index.d.cts +631 -0
- package/dist/model/index.d.ts +631 -0
- package/dist/model/index.js +21 -0
- package/dist/model/index.js.map +1 -0
- package/dist/styled-DZ7GjShb.d.ts +282 -0
- package/dist/styled-ZTbdaVFM.d.cts +282 -0
- package/dist/theory/index.cjs +1823 -0
- package/dist/theory/index.cjs.map +1 -0
- package/dist/theory/index.d.cts +535 -0
- package/dist/theory/index.d.ts +535 -0
- package/dist/theory/index.js +150 -0
- package/dist/theory/index.js.map +1 -0
- package/dist/types-MGP29r3t.d.cts +30 -0
- package/dist/types-MGP29r3t.d.ts +30 -0
- package/package.json +61 -5
- package/src/analyze/arrange/index.ts +21 -0
- package/src/analyze/arrange/internal.ts +127 -0
- package/src/analyze/arrange/tension.ts +199 -0
- package/src/analyze/arrange/tracks.ts +312 -0
- package/src/{detect → analyze/detect}/index.ts +50 -13
- package/src/analyze/functional/borrowed.ts +84 -0
- package/src/analyze/functional/cadence.ts +66 -0
- package/src/analyze/functional/function.ts +184 -0
- package/src/analyze/functional/index.ts +23 -0
- package/src/analyze/functional/internal.ts +29 -0
- package/src/{functional/index.ts → analyze/functional/roman.ts} +23 -332
- package/src/analyze/index.ts +49 -0
- package/src/{timeline → analyze/timeline}/index.ts +50 -18
- package/src/{analysis → analyze/voice}/index.ts +19 -7
- package/src/core/event-index/index.ts +104 -0
- package/src/core/index.ts +61 -0
- package/src/{meter → core/meter}/index.ts +24 -4
- package/src/{random → core/random}/index.ts +26 -3
- package/src/{tuning → core/tuning}/index.ts +36 -6
- package/src/core/validation/index.ts +119 -0
- package/src/{bass → generate/bass}/index.ts +28 -5
- package/src/{countermelody → generate/countermelody}/index.ts +176 -85
- package/src/{drums → generate/drums}/euclid.ts +10 -3
- package/src/{drums → generate/drums}/index.ts +58 -26
- package/src/{drums → generate/drums}/percussion.ts +1 -1
- package/src/{drums → generate/drums}/rng.ts +2 -2
- package/src/{groove → generate/groove}/index.ts +28 -3
- package/src/{harmonize → generate/harmonize}/index.ts +68 -57
- package/src/generate/index.ts +58 -0
- package/src/{motif → generate/motif}/index.ts +52 -28
- package/src/{progression → generate/progression}/index.ts +19 -41
- package/src/{reharmony → generate/reharmony}/index.ts +12 -19
- package/src/{rhythm → generate/rhythm}/index.ts +13 -3
- package/src/index.ts +13 -234
- package/src/model/chord.ts +442 -0
- package/src/model/index.ts +5 -1096
- package/src/model/interval.ts +82 -0
- package/src/model/key.ts +266 -0
- package/src/model/note.ts +176 -0
- package/src/model/progression.ts +161 -0
- package/src/model/shared.ts +12 -0
- package/src/{chord → theory/chord}/index.ts +23 -3
- package/src/{counterpoint → theory/counterpoint}/index.ts +2 -2
- package/src/{harmony → theory/harmony}/index.ts +6 -21
- package/src/theory/index.ts +112 -0
- package/src/{safety → theory/safety}/index.ts +72 -3
- package/src/theory/scale/degrees.ts +123 -0
- package/src/theory/scale/index.ts +28 -0
- package/src/theory/scale/key.ts +56 -0
- package/src/theory/scale/masks.ts +153 -0
- package/src/{spelling → theory/spelling}/index.ts +73 -6
- package/src/{symbol → theory/symbol}/index.ts +8 -1
- package/src/theory/voicing/index.ts +5 -0
- package/src/theory/voicing/internal.ts +168 -0
- package/src/theory/voicing/leading.ts +112 -0
- package/src/theory/voicing/satb.ts +216 -0
- package/src/theory/voicing/styled.ts +199 -0
- package/src/arrange/index.ts +0 -571
- package/src/scale/index.ts +0 -331
- package/src/voicing/index.ts +0 -623
- /package/src/{interval → core/interval}/index.ts +0 -0
- /package/src/{pitch → core/pitch}/index.ts +0 -0
- /package/src/{types.ts → core/types.ts} +0 -0
- /package/src/{drums → generate/drums}/beat.ts +0 -0
- /package/src/{drums → generate/drums}/fills.ts +0 -0
- /package/src/{drums → generate/drums}/ghost.ts +0 -0
- /package/src/{drums → generate/drums}/hihat.ts +0 -0
- /package/src/{drums → generate/drums}/hit.ts +0 -0
- /package/src/{drums → generate/drums}/internal.ts +0 -0
- /package/src/{drums → generate/drums}/kick.ts +0 -0
- /package/src/{drums → generate/drums}/swing.ts +0 -0
- /package/src/{chordscale → theory/chordscale}/index.ts +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pitch spelling: letter-name notes, enharmonic-aware conversion, and spelled
|
|
3
|
+
* intervals.
|
|
4
|
+
*
|
|
5
|
+
* The rest of the library works in pitch classes (0..11), which cannot tell a
|
|
6
|
+
* G# from an Ab or an augmented fourth from a diminished fifth. This module adds
|
|
7
|
+
* an explicit spelling layer — a note is a diatonic letter plus a chromatic
|
|
8
|
+
* alteration — so interval quality (P/M/m/A/d) and enharmonic identity are
|
|
9
|
+
* preserved.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* A spelled note: a diatonic letter, a chromatic alteration, and an optional
|
|
13
|
+
* octave.
|
|
14
|
+
*
|
|
15
|
+
* - `letter` is 0..6 for C..B.
|
|
16
|
+
* - `alter` is the number of semitones of alteration: -2 double-flat, -1 flat,
|
|
17
|
+
* 0 natural, +1 sharp, +2 double-sharp.
|
|
18
|
+
* - `octave` follows scientific pitch notation (middle C = C4 = MIDI 60). When
|
|
19
|
+
* absent, the note denotes a bare pitch class.
|
|
20
|
+
*
|
|
21
|
+
* @category Pitch & Intervals
|
|
22
|
+
*/
|
|
23
|
+
type Note = {
|
|
24
|
+
letter: number;
|
|
25
|
+
alter: number;
|
|
26
|
+
octave?: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A spelled interval: a diatonic number, a quality label, and its semitone span.
|
|
30
|
+
*
|
|
31
|
+
* @category Pitch & Intervals
|
|
32
|
+
*/
|
|
33
|
+
type SpelledInterval = {
|
|
34
|
+
/** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
|
|
35
|
+
number: number;
|
|
36
|
+
/** Quality label: 'P', 'M', 'm', or repeated 'A'/'d' for (multiply) aug/dim. */
|
|
37
|
+
quality: string;
|
|
38
|
+
/** Signed semitone distance from the first note to the second. */
|
|
39
|
+
semitones: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Parse scientific pitch notation into a {@link Note}.
|
|
43
|
+
*
|
|
44
|
+
* Accepts a letter (case-insensitive) followed by any number of same-direction
|
|
45
|
+
* accidentals and an optional octave, e.g. `"C"`, `"C#4"`, `"Bb"`, `"F##3"`,
|
|
46
|
+
* `"Ebb2"`. Both `#` and `x` (double-sharp) are accepted for sharps; `b` for
|
|
47
|
+
* flats. Mixing sharps and flats (e.g. `"C#b"`) is rejected.
|
|
48
|
+
*
|
|
49
|
+
* @param text The note text.
|
|
50
|
+
* @returns The parsed note.
|
|
51
|
+
* @throws If the text is not a valid note.
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { parseNote, noteToMidi } from '@libraz/libcantus';
|
|
55
|
+
* const n = parseNote('C#4');
|
|
56
|
+
* noteToMidi(n); // 61
|
|
57
|
+
* ```
|
|
58
|
+
* @category Pitch & Intervals
|
|
59
|
+
*/
|
|
60
|
+
declare function parseNote(text: string): Note;
|
|
61
|
+
/**
|
|
62
|
+
* Render a {@link Note} as scientific pitch notation.
|
|
63
|
+
*
|
|
64
|
+
* @param note The note to format.
|
|
65
|
+
* @returns The note text, including the octave when present.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { formatNote, parseNote } from '@libraz/libcantus';
|
|
69
|
+
* formatNote(parseNote('C#4')); // 'C#4'
|
|
70
|
+
* ```
|
|
71
|
+
* @category Pitch & Intervals
|
|
72
|
+
*/
|
|
73
|
+
declare function formatNote(note: Note): string;
|
|
74
|
+
/**
|
|
75
|
+
* Pitch class (0..11) of a note, ignoring octave.
|
|
76
|
+
*
|
|
77
|
+
* @param note The note.
|
|
78
|
+
* @returns The pitch class.
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* import { noteToPitchClass, parseNote } from '@libraz/libcantus';
|
|
82
|
+
* noteToPitchClass(parseNote('Db')); // 1
|
|
83
|
+
* ```
|
|
84
|
+
* @category Pitch & Intervals
|
|
85
|
+
*/
|
|
86
|
+
declare function noteToPitchClass(note: Note): number;
|
|
87
|
+
/**
|
|
88
|
+
* MIDI number of a note (middle C = C4 = 60).
|
|
89
|
+
*
|
|
90
|
+
* @param note The note; must carry an octave.
|
|
91
|
+
* @returns The MIDI number.
|
|
92
|
+
* @throws If the note has no octave.
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { noteToMidi, parseNote } from '@libraz/libcantus';
|
|
96
|
+
* noteToMidi(parseNote('A4')); // 69
|
|
97
|
+
* ```
|
|
98
|
+
* @category Pitch & Intervals
|
|
99
|
+
*/
|
|
100
|
+
declare function noteToMidi(note: Note): number;
|
|
101
|
+
/**
|
|
102
|
+
* Name a MIDI number as a {@link Note}, choosing sharp or flat spelling.
|
|
103
|
+
*
|
|
104
|
+
* The input is rounded to the nearest integer but is not clamped to the MIDI
|
|
105
|
+
* range 0..127: out-of-range values extrapolate linearly (e.g. `-1` -> `B-2`,
|
|
106
|
+
* `128` -> `G#9`) and remain an exact inverse of {@link noteToMidi}.
|
|
107
|
+
*
|
|
108
|
+
* @param midi The MIDI number.
|
|
109
|
+
* @param spelling Whether to prefer sharps or flats for black keys.
|
|
110
|
+
* @returns The spelled note, with octave.
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* import { midiToNote, formatNote } from '@libraz/libcantus';
|
|
114
|
+
* formatNote(midiToNote(61)); // 'C#4'
|
|
115
|
+
* formatNote(midiToNote(61, 'flat')); // 'Db4'
|
|
116
|
+
* ```
|
|
117
|
+
* @category Pitch & Intervals
|
|
118
|
+
*/
|
|
119
|
+
declare function midiToNote(midi: number, spelling?: 'sharp' | 'flat'): Note;
|
|
120
|
+
/**
|
|
121
|
+
* The spelled interval from one note to another.
|
|
122
|
+
*
|
|
123
|
+
* When both notes carry octaves, the interval reflects their full signed
|
|
124
|
+
* distance (so `C4 -> C5` is a perfect octave). When either lacks an octave the
|
|
125
|
+
* interval is measured within a single ascending octave from `a` to `b`.
|
|
126
|
+
*
|
|
127
|
+
* @param a The lower/first note.
|
|
128
|
+
* @param b The upper/second note.
|
|
129
|
+
* @returns The diatonic number, quality, and semitone span.
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { spelledInterval, parseNote } from '@libraz/libcantus';
|
|
133
|
+
* spelledInterval(parseNote('C4'), parseNote('G4'));
|
|
134
|
+
* // { number: 5, quality: 'P', semitones: 7 }
|
|
135
|
+
* ```
|
|
136
|
+
* @category Pitch & Intervals
|
|
137
|
+
*/
|
|
138
|
+
declare function spelledInterval(a: Note, b: Note): SpelledInterval;
|
|
139
|
+
|
|
140
|
+
export { type Note as N, type SpelledInterval as S, noteToPitchClass as a, formatNote as f, midiToNote as m, noteToMidi as n, parseNote as p, spelledInterval as s };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meter and time signatures: bar/beat arithmetic, simple vs. compound meters,
|
|
3
|
+
* metric-accent hierarchy, and tuplet subdivision.
|
|
4
|
+
*
|
|
5
|
+
* Positions and durations are measured in quarter-note beats, matching the rest
|
|
6
|
+
* of the library's beat convention (four quarter-note beats per bar in 4/4).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A time signature as a numerator over a note-value denominator.
|
|
10
|
+
*
|
|
11
|
+
* @category Rhythm & Meter
|
|
12
|
+
*/
|
|
13
|
+
type TimeSignature = {
|
|
14
|
+
numerator: number;
|
|
15
|
+
denominator: number;
|
|
16
|
+
/**
|
|
17
|
+
* Optional additive grouping of the bar's main pulses, as the felt-beat
|
|
18
|
+
* lengths in pulses — e.g. `[2, 2, 3]` for a 2+2+3 reading of 7/8, or
|
|
19
|
+
* `[3, 2]` for 5/8. The entries must be positive integers summing to
|
|
20
|
+
* {@link pulsesPerBar}. When present, the head pulse of each group (other
|
|
21
|
+
* than the downbeat) is treated as a secondary strong pulse by
|
|
22
|
+
* {@link metricWeight}/{@link isStrongBeat}; when absent, all main pulses
|
|
23
|
+
* are weighted equally as flat, evenly divided pulses.
|
|
24
|
+
*/
|
|
25
|
+
grouping?: number[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A position expressed as a bar index and a quarter-note offset within the bar.
|
|
29
|
+
*
|
|
30
|
+
* @category Rhythm & Meter
|
|
31
|
+
*/
|
|
32
|
+
type BarPosition = {
|
|
33
|
+
bar: number;
|
|
34
|
+
/** Quarter-note offset from the start of the bar. */
|
|
35
|
+
beat: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse a time signature such as `"4/4"` or `"6/8"`.
|
|
39
|
+
*
|
|
40
|
+
* @param text The signature text.
|
|
41
|
+
* @returns The parsed time signature.
|
|
42
|
+
* @throws If the text is not `n/d` with positive integers.
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { parseTimeSignature } from '@libraz/libcantus';
|
|
46
|
+
* parseTimeSignature('6/8'); // { numerator: 6, denominator: 8 }
|
|
47
|
+
* ```
|
|
48
|
+
* @category Rhythm & Meter
|
|
49
|
+
*/
|
|
50
|
+
declare function parseTimeSignature(text: string): TimeSignature;
|
|
51
|
+
/**
|
|
52
|
+
* Render a time signature as `"n/d"`.
|
|
53
|
+
*
|
|
54
|
+
* @category Rhythm & Meter
|
|
55
|
+
*/
|
|
56
|
+
declare function formatTimeSignature(ts: TimeSignature): string;
|
|
57
|
+
/**
|
|
58
|
+
* Whether a meter is compound: its main pulses each divide into three, as in
|
|
59
|
+
* 6/8, 9/8, 12/8, or 6/4. Compound meters are those whose numerator is a
|
|
60
|
+
* multiple of three greater than three, independent of the denominator. Meters
|
|
61
|
+
* like 3/8 and 3/4 (simple triples, numerator 3) are not compound.
|
|
62
|
+
*
|
|
63
|
+
* @param ts The time signature.
|
|
64
|
+
* @returns True for compound meters.
|
|
65
|
+
* @category Rhythm & Meter
|
|
66
|
+
*/
|
|
67
|
+
declare function isCompound(ts: TimeSignature): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Length of a bar in quarter-note beats.
|
|
70
|
+
*
|
|
71
|
+
* @param ts The time signature.
|
|
72
|
+
* @returns The bar length in quarter notes.
|
|
73
|
+
* @category Rhythm & Meter
|
|
74
|
+
*/
|
|
75
|
+
declare function beatsPerBar(ts: TimeSignature): number;
|
|
76
|
+
/**
|
|
77
|
+
* Number of main pulses (felt beats) per bar: the numerator for simple meters,
|
|
78
|
+
* a third of it for compound meters.
|
|
79
|
+
*
|
|
80
|
+
* For additive/irregular meters such as 7/8 or 5/8 this counts the raw pulses
|
|
81
|
+
* (7 and 5 respectively), which are otherwise felt as flat, evenly divided and
|
|
82
|
+
* equally accented pulses. To recover a 2+2+3 or 3+2 felt-beat grouping, set
|
|
83
|
+
* {@link TimeSignature.grouping}; {@link metricWeight} then accents each
|
|
84
|
+
* group's head pulse.
|
|
85
|
+
*
|
|
86
|
+
* @param ts The time signature.
|
|
87
|
+
* @returns The pulse count per bar.
|
|
88
|
+
* @category Rhythm & Meter
|
|
89
|
+
*/
|
|
90
|
+
declare function pulsesPerBar(ts: TimeSignature): number;
|
|
91
|
+
/**
|
|
92
|
+
* Convert an absolute quarter-note position to a bar index and in-bar offset.
|
|
93
|
+
*
|
|
94
|
+
* @param beatInQuarters Absolute position in quarter-note beats.
|
|
95
|
+
* @param ts The time signature.
|
|
96
|
+
* @returns The bar and in-bar quarter-note offset.
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { parseTimeSignature, beatToBarPosition } from '@libraz/libcantus';
|
|
100
|
+
* const ts = parseTimeSignature('4/4');
|
|
101
|
+
* beatToBarPosition(5, ts); // { bar: 1, beat: 1 }
|
|
102
|
+
* ```
|
|
103
|
+
* @category Rhythm & Meter
|
|
104
|
+
*/
|
|
105
|
+
declare function beatToBarPosition(beatInQuarters: number, ts: TimeSignature): BarPosition;
|
|
106
|
+
/**
|
|
107
|
+
* Convert a bar index and in-bar offset back to an absolute quarter-note
|
|
108
|
+
* position.
|
|
109
|
+
*
|
|
110
|
+
* @param pos The bar position.
|
|
111
|
+
* @param ts The time signature.
|
|
112
|
+
* @returns The absolute position in quarter-note beats.
|
|
113
|
+
* @category Rhythm & Meter
|
|
114
|
+
*/
|
|
115
|
+
declare function barPositionToBeat(pos: BarPosition, ts: TimeSignature): number;
|
|
116
|
+
/**
|
|
117
|
+
* Metric weight of a position within its bar, on a 0–3 scale:
|
|
118
|
+
* 3 the downbeat, 2 a secondary strong pulse, 1 any other main pulse, and 0 an
|
|
119
|
+
* off-pulse subdivision.
|
|
120
|
+
*
|
|
121
|
+
* The secondary strong pulse is the bar's midpoint in even simple/compound
|
|
122
|
+
* meters. For an additive/irregular meter with {@link TimeSignature.grouping}
|
|
123
|
+
* set (e.g. `[2, 2, 3]` for 7/8), each group's head pulse other than the
|
|
124
|
+
* downbeat is the secondary strong pulse instead; without a grouping every
|
|
125
|
+
* non-downbeat main pulse of such a meter weighs 1 (flat, evenly divided
|
|
126
|
+
* pulses).
|
|
127
|
+
*
|
|
128
|
+
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
129
|
+
* @param ts The time signature.
|
|
130
|
+
* @returns The metric weight (0–3).
|
|
131
|
+
* @throws If `ts.grouping` is present but is not a positive-integer list
|
|
132
|
+
* summing to {@link pulsesPerBar}.
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { parseTimeSignature, metricWeight } from '@libraz/libcantus';
|
|
136
|
+
* const ts = parseTimeSignature('4/4');
|
|
137
|
+
* metricWeight(0, ts); // 3 (the downbeat)
|
|
138
|
+
* ```
|
|
139
|
+
* @category Rhythm & Meter
|
|
140
|
+
*/
|
|
141
|
+
declare function metricWeight(beatInQuarters: number, ts: TimeSignature): number;
|
|
142
|
+
/**
|
|
143
|
+
* Whether a position is metrically accented (weight 2 or more — a downbeat or a
|
|
144
|
+
* secondary strong pulse).
|
|
145
|
+
*
|
|
146
|
+
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
147
|
+
* @param ts The time signature.
|
|
148
|
+
* @returns True on strong beats.
|
|
149
|
+
* @category Rhythm & Meter
|
|
150
|
+
*/
|
|
151
|
+
declare function isStrongBeat(beatInQuarters: number, ts: TimeSignature): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Subdivide a span into `count` equal tuplet durations (e.g. an eighth-note
|
|
154
|
+
* triplet is `tuplet(1, 3)`).
|
|
155
|
+
*
|
|
156
|
+
* @param totalBeats Total span in quarter-note beats.
|
|
157
|
+
* @param count Number of equal parts.
|
|
158
|
+
* @returns `count` equal durations summing to `totalBeats`.
|
|
159
|
+
* @throws If `count` is not a positive integer.
|
|
160
|
+
* @category Rhythm & Meter
|
|
161
|
+
*/
|
|
162
|
+
declare function tuplet(totalBeats: number, count: number): number[];
|
|
163
|
+
|
|
164
|
+
export { type BarPosition as B, type TimeSignature as T, beatToBarPosition as a, barPositionToBeat as b, beatsPerBar as c, isStrongBeat as d, pulsesPerBar as e, formatTimeSignature as f, isCompound as i, metricWeight as m, parseTimeSignature as p, tuplet as t };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meter and time signatures: bar/beat arithmetic, simple vs. compound meters,
|
|
3
|
+
* metric-accent hierarchy, and tuplet subdivision.
|
|
4
|
+
*
|
|
5
|
+
* Positions and durations are measured in quarter-note beats, matching the rest
|
|
6
|
+
* of the library's beat convention (four quarter-note beats per bar in 4/4).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A time signature as a numerator over a note-value denominator.
|
|
10
|
+
*
|
|
11
|
+
* @category Rhythm & Meter
|
|
12
|
+
*/
|
|
13
|
+
type TimeSignature = {
|
|
14
|
+
numerator: number;
|
|
15
|
+
denominator: number;
|
|
16
|
+
/**
|
|
17
|
+
* Optional additive grouping of the bar's main pulses, as the felt-beat
|
|
18
|
+
* lengths in pulses — e.g. `[2, 2, 3]` for a 2+2+3 reading of 7/8, or
|
|
19
|
+
* `[3, 2]` for 5/8. The entries must be positive integers summing to
|
|
20
|
+
* {@link pulsesPerBar}. When present, the head pulse of each group (other
|
|
21
|
+
* than the downbeat) is treated as a secondary strong pulse by
|
|
22
|
+
* {@link metricWeight}/{@link isStrongBeat}; when absent, all main pulses
|
|
23
|
+
* are weighted equally as flat, evenly divided pulses.
|
|
24
|
+
*/
|
|
25
|
+
grouping?: number[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A position expressed as a bar index and a quarter-note offset within the bar.
|
|
29
|
+
*
|
|
30
|
+
* @category Rhythm & Meter
|
|
31
|
+
*/
|
|
32
|
+
type BarPosition = {
|
|
33
|
+
bar: number;
|
|
34
|
+
/** Quarter-note offset from the start of the bar. */
|
|
35
|
+
beat: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse a time signature such as `"4/4"` or `"6/8"`.
|
|
39
|
+
*
|
|
40
|
+
* @param text The signature text.
|
|
41
|
+
* @returns The parsed time signature.
|
|
42
|
+
* @throws If the text is not `n/d` with positive integers.
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { parseTimeSignature } from '@libraz/libcantus';
|
|
46
|
+
* parseTimeSignature('6/8'); // { numerator: 6, denominator: 8 }
|
|
47
|
+
* ```
|
|
48
|
+
* @category Rhythm & Meter
|
|
49
|
+
*/
|
|
50
|
+
declare function parseTimeSignature(text: string): TimeSignature;
|
|
51
|
+
/**
|
|
52
|
+
* Render a time signature as `"n/d"`.
|
|
53
|
+
*
|
|
54
|
+
* @category Rhythm & Meter
|
|
55
|
+
*/
|
|
56
|
+
declare function formatTimeSignature(ts: TimeSignature): string;
|
|
57
|
+
/**
|
|
58
|
+
* Whether a meter is compound: its main pulses each divide into three, as in
|
|
59
|
+
* 6/8, 9/8, 12/8, or 6/4. Compound meters are those whose numerator is a
|
|
60
|
+
* multiple of three greater than three, independent of the denominator. Meters
|
|
61
|
+
* like 3/8 and 3/4 (simple triples, numerator 3) are not compound.
|
|
62
|
+
*
|
|
63
|
+
* @param ts The time signature.
|
|
64
|
+
* @returns True for compound meters.
|
|
65
|
+
* @category Rhythm & Meter
|
|
66
|
+
*/
|
|
67
|
+
declare function isCompound(ts: TimeSignature): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Length of a bar in quarter-note beats.
|
|
70
|
+
*
|
|
71
|
+
* @param ts The time signature.
|
|
72
|
+
* @returns The bar length in quarter notes.
|
|
73
|
+
* @category Rhythm & Meter
|
|
74
|
+
*/
|
|
75
|
+
declare function beatsPerBar(ts: TimeSignature): number;
|
|
76
|
+
/**
|
|
77
|
+
* Number of main pulses (felt beats) per bar: the numerator for simple meters,
|
|
78
|
+
* a third of it for compound meters.
|
|
79
|
+
*
|
|
80
|
+
* For additive/irregular meters such as 7/8 or 5/8 this counts the raw pulses
|
|
81
|
+
* (7 and 5 respectively), which are otherwise felt as flat, evenly divided and
|
|
82
|
+
* equally accented pulses. To recover a 2+2+3 or 3+2 felt-beat grouping, set
|
|
83
|
+
* {@link TimeSignature.grouping}; {@link metricWeight} then accents each
|
|
84
|
+
* group's head pulse.
|
|
85
|
+
*
|
|
86
|
+
* @param ts The time signature.
|
|
87
|
+
* @returns The pulse count per bar.
|
|
88
|
+
* @category Rhythm & Meter
|
|
89
|
+
*/
|
|
90
|
+
declare function pulsesPerBar(ts: TimeSignature): number;
|
|
91
|
+
/**
|
|
92
|
+
* Convert an absolute quarter-note position to a bar index and in-bar offset.
|
|
93
|
+
*
|
|
94
|
+
* @param beatInQuarters Absolute position in quarter-note beats.
|
|
95
|
+
* @param ts The time signature.
|
|
96
|
+
* @returns The bar and in-bar quarter-note offset.
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { parseTimeSignature, beatToBarPosition } from '@libraz/libcantus';
|
|
100
|
+
* const ts = parseTimeSignature('4/4');
|
|
101
|
+
* beatToBarPosition(5, ts); // { bar: 1, beat: 1 }
|
|
102
|
+
* ```
|
|
103
|
+
* @category Rhythm & Meter
|
|
104
|
+
*/
|
|
105
|
+
declare function beatToBarPosition(beatInQuarters: number, ts: TimeSignature): BarPosition;
|
|
106
|
+
/**
|
|
107
|
+
* Convert a bar index and in-bar offset back to an absolute quarter-note
|
|
108
|
+
* position.
|
|
109
|
+
*
|
|
110
|
+
* @param pos The bar position.
|
|
111
|
+
* @param ts The time signature.
|
|
112
|
+
* @returns The absolute position in quarter-note beats.
|
|
113
|
+
* @category Rhythm & Meter
|
|
114
|
+
*/
|
|
115
|
+
declare function barPositionToBeat(pos: BarPosition, ts: TimeSignature): number;
|
|
116
|
+
/**
|
|
117
|
+
* Metric weight of a position within its bar, on a 0–3 scale:
|
|
118
|
+
* 3 the downbeat, 2 a secondary strong pulse, 1 any other main pulse, and 0 an
|
|
119
|
+
* off-pulse subdivision.
|
|
120
|
+
*
|
|
121
|
+
* The secondary strong pulse is the bar's midpoint in even simple/compound
|
|
122
|
+
* meters. For an additive/irregular meter with {@link TimeSignature.grouping}
|
|
123
|
+
* set (e.g. `[2, 2, 3]` for 7/8), each group's head pulse other than the
|
|
124
|
+
* downbeat is the secondary strong pulse instead; without a grouping every
|
|
125
|
+
* non-downbeat main pulse of such a meter weighs 1 (flat, evenly divided
|
|
126
|
+
* pulses).
|
|
127
|
+
*
|
|
128
|
+
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
129
|
+
* @param ts The time signature.
|
|
130
|
+
* @returns The metric weight (0–3).
|
|
131
|
+
* @throws If `ts.grouping` is present but is not a positive-integer list
|
|
132
|
+
* summing to {@link pulsesPerBar}.
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { parseTimeSignature, metricWeight } from '@libraz/libcantus';
|
|
136
|
+
* const ts = parseTimeSignature('4/4');
|
|
137
|
+
* metricWeight(0, ts); // 3 (the downbeat)
|
|
138
|
+
* ```
|
|
139
|
+
* @category Rhythm & Meter
|
|
140
|
+
*/
|
|
141
|
+
declare function metricWeight(beatInQuarters: number, ts: TimeSignature): number;
|
|
142
|
+
/**
|
|
143
|
+
* Whether a position is metrically accented (weight 2 or more — a downbeat or a
|
|
144
|
+
* secondary strong pulse).
|
|
145
|
+
*
|
|
146
|
+
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
147
|
+
* @param ts The time signature.
|
|
148
|
+
* @returns True on strong beats.
|
|
149
|
+
* @category Rhythm & Meter
|
|
150
|
+
*/
|
|
151
|
+
declare function isStrongBeat(beatInQuarters: number, ts: TimeSignature): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Subdivide a span into `count` equal tuplet durations (e.g. an eighth-note
|
|
154
|
+
* triplet is `tuplet(1, 3)`).
|
|
155
|
+
*
|
|
156
|
+
* @param totalBeats Total span in quarter-note beats.
|
|
157
|
+
* @param count Number of equal parts.
|
|
158
|
+
* @returns `count` equal durations summing to `totalBeats`.
|
|
159
|
+
* @throws If `count` is not a positive integer.
|
|
160
|
+
* @category Rhythm & Meter
|
|
161
|
+
*/
|
|
162
|
+
declare function tuplet(totalBeats: number, count: number): number[];
|
|
163
|
+
|
|
164
|
+
export { type BarPosition as B, type TimeSignature as T, beatToBarPosition as a, barPositionToBeat as b, beatsPerBar as c, isStrongBeat as d, pulsesPerBar as e, formatTimeSignature as f, isCompound as i, metricWeight as m, parseTimeSignature as p, tuplet as t };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { K as KeyScale } from './types-MGP29r3t.cjs';
|
|
2
|
+
import { a as ChordQuality, C as Chord } from './index-BNlGP86A.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Recognition: infer a chord name from a set of pitches, or a key from a set of
|
|
6
|
+
* pitch classes. This is the inverse direction of the chord/scale builders,
|
|
7
|
+
* which only go name -> notes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A candidate chord interpretation of a pitch set.
|
|
12
|
+
*
|
|
13
|
+
* @category Recognition
|
|
14
|
+
*/
|
|
15
|
+
type ChordMatch = {
|
|
16
|
+
rootPc: number;
|
|
17
|
+
quality: ChordQuality;
|
|
18
|
+
/** Chord tones absent from the input (an incomplete voicing). */
|
|
19
|
+
missingPcs: number[];
|
|
20
|
+
/** Input pitch classes not belonging to the chord. */
|
|
21
|
+
extraPcs: number[];
|
|
22
|
+
/** True when the input pitch-class set equals the chord exactly. */
|
|
23
|
+
exact: boolean;
|
|
24
|
+
/** Inversion implied by a known bass, or null for an unordered pitch-class set. */
|
|
25
|
+
inversion: number | null;
|
|
26
|
+
/** Bass pitch class when inverted (the lowest note is not the root). */
|
|
27
|
+
bassPc?: number;
|
|
28
|
+
};
|
|
29
|
+
/** Input interpretation for {@link detectChord} and {@link detectChordBest}. */
|
|
30
|
+
type DetectChordOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* `midi` uses the numerically lowest pitch as bass; `pitchClass` treats the
|
|
33
|
+
* input as unordered. `auto` (default) selects pitch-class mode only when all
|
|
34
|
+
* values lie in 0..11.
|
|
35
|
+
*/
|
|
36
|
+
input?: 'auto' | 'midi' | 'pitchClass';
|
|
37
|
+
/** Explicit bass pitch class, including for an unordered pitch-class set. */
|
|
38
|
+
bassPc?: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A candidate key interpretation of a pitch-class set.
|
|
42
|
+
*
|
|
43
|
+
* @category Recognition
|
|
44
|
+
*/
|
|
45
|
+
type KeyMatch = {
|
|
46
|
+
key: KeyScale;
|
|
47
|
+
mode: 'major' | 'minor';
|
|
48
|
+
/** Fraction of input pitch classes that are in the scale, in [0, 1]. */
|
|
49
|
+
fit: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Identify chords matching a set of pitches.
|
|
53
|
+
*
|
|
54
|
+
* Every input pitch class is tried as a root against every known chord quality.
|
|
55
|
+
* A match is reported when all of the chord's tones are present in the input, or
|
|
56
|
+
* when the only absent tone is the perfect fifth (a common omission in shell
|
|
57
|
+
* voicings); matches are ranked best-first by fewest extra notes, then fewest
|
|
58
|
+
* missing notes, then most specific (largest) chord. An exact match (no extras,
|
|
59
|
+
* no missing) is flagged and ranked first.
|
|
60
|
+
*
|
|
61
|
+
* @param pitches MIDI pitches or bare pitch classes (octave-agnostic).
|
|
62
|
+
* @returns Ranked chord interpretations (may be empty).
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { detectChord } from '@libraz/libcantus';
|
|
66
|
+
* const matches = detectChord([60, 64, 67]); // C E G
|
|
67
|
+
* matches[0]; // { rootPc: 0, quality: 'maj', exact: true, ... }
|
|
68
|
+
* ```
|
|
69
|
+
* @category Recognition
|
|
70
|
+
*/
|
|
71
|
+
declare function detectChord(pitches: number[], opts?: DetectChordOptions): ChordMatch[];
|
|
72
|
+
/**
|
|
73
|
+
* The single best chord interpretation of a pitch set, or null if none.
|
|
74
|
+
*
|
|
75
|
+
* @param pitches MIDI pitches or bare pitch classes.
|
|
76
|
+
* @returns The top-ranked chord, or null when nothing matches.
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { detectChordBest } from '@libraz/libcantus';
|
|
80
|
+
* detectChordBest([60, 64, 67]); // C major triad: { rootPc: 0, quality: 'maj', ... }
|
|
81
|
+
* ```
|
|
82
|
+
* @category Recognition
|
|
83
|
+
*/
|
|
84
|
+
declare function detectChordBest(pitches: number[], opts?: DetectChordOptions): Chord | null;
|
|
85
|
+
/**
|
|
86
|
+
* Rank major and minor keys by how well they contain a set of pitch classes.
|
|
87
|
+
*
|
|
88
|
+
* The tonic is weighted so that, among equally-fitting keys, the one whose root
|
|
89
|
+
* appears in the input is preferred. Minor candidates are scored against the
|
|
90
|
+
* natural, harmonic, and melodic minor variants and keep the best of the three,
|
|
91
|
+
* so a minor cadence containing the leading tone still resolves to its own
|
|
92
|
+
* tonic; the returned key is always the natural-minor scale. Returns all 24
|
|
93
|
+
* keys ranked best-first, or an empty array for an empty input (mirroring
|
|
94
|
+
* {@link detectChord}).
|
|
95
|
+
*
|
|
96
|
+
* @param pitches MIDI pitches or bare pitch classes.
|
|
97
|
+
* @returns Ranked key interpretations (empty for an empty input).
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { detectKey } from '@libraz/libcantus';
|
|
101
|
+
* const keys = detectKey([60, 62, 64, 65, 67, 69, 71]); // C major scale
|
|
102
|
+
* keys[0].mode; // 'major', with keys[0].key.rootPc === 0
|
|
103
|
+
* ```
|
|
104
|
+
* @category Recognition
|
|
105
|
+
*/
|
|
106
|
+
declare function detectKey(pitches: number[]): KeyMatch[];
|
|
107
|
+
|
|
108
|
+
export { type ChordMatch as C, type DetectChordOptions as D, type KeyMatch as K, detectChordBest as a, detectKey as b, detectChord as d };
|