@libraz/libcantus 0.9.0 → 0.9.1
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 +10 -2
- package/dist/index.cjs +577 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1533 -172
- package/dist/index.d.ts +1533 -172
- package/dist/index.js +577 -225
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/src/analysis/index.ts +73 -7
- package/src/arrange/index.ts +241 -79
- package/src/bass/index.ts +61 -12
- package/src/chord/index.ts +66 -3
- package/src/chordscale/index.ts +67 -9
- package/src/countermelody/index.ts +44 -14
- package/src/counterpoint/index.ts +21 -0
- package/src/detect/index.ts +79 -20
- package/src/drums/fills.ts +31 -20
- package/src/drums/index.ts +125 -7
- package/src/drums/internal.ts +7 -1
- package/src/drums/swing.ts +7 -2
- package/src/functional/index.ts +160 -51
- package/src/groove/index.ts +116 -20
- package/src/harmonize/index.ts +85 -12
- package/src/harmony/index.ts +19 -2
- package/src/index.ts +1 -0
- package/src/interval/index.ts +15 -1
- package/src/meter/index.ts +108 -5
- package/src/model/index.ts +161 -12
- package/src/motif/index.ts +87 -10
- package/src/pitch/index.ts +58 -3
- package/src/progression/index.ts +56 -8
- package/src/random/index.ts +13 -1
- package/src/reharmony/index.ts +35 -4
- package/src/rhythm/index.ts +45 -11
- package/src/safety/index.ts +58 -11
- package/src/scale/index.ts +135 -19
- package/src/spelling/index.ts +18 -0
- package/src/symbol/index.ts +84 -36
- package/src/timeline/index.ts +80 -13
- package/src/tuning/index.ts +28 -1
- package/src/voicing/index.ts +121 -21
package/dist/index.d.ts
CHANGED
|
@@ -27,16 +27,47 @@ type NoteEvent = {
|
|
|
27
27
|
velocity?: number;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Chord quality identifiers understood by the chord builder.
|
|
32
|
+
*
|
|
33
|
+
* @category Chords
|
|
34
|
+
*/
|
|
31
35
|
type ChordQuality = 'maj' | 'min' | 'dim' | 'aug' | 'maj7' | 'min7' | 'dom7' | 'dim7' | 'm7b5' | 'minMaj7' | 'aug7' | 'augMaj7' | 'majb5' | '6' | 'min6' | '6/9' | 'sus2' | 'sus4' | 'add9' | 'add11' | 'maj9' | 'min9' | 'dom9' | '7b9' | '7#9' | '7#11' | '7b13' | '11' | '13' | '5';
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* A bare spelled pitch used as an enharmonic hint: a diatonic letter
|
|
38
|
+
* (0..6 = C..B) plus a chromatic alteration (-2 double-flat .. +2 double-sharp).
|
|
39
|
+
* Mirrors the letter/alter half of the pitch module's `Note` without an octave.
|
|
40
|
+
*
|
|
41
|
+
* @category Chords
|
|
42
|
+
*/
|
|
43
|
+
type PitchSpelling = {
|
|
44
|
+
letter: number;
|
|
45
|
+
alter: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A chord expressed as a root pitch class plus semitone offsets.
|
|
49
|
+
*
|
|
50
|
+
* `rootSpelling`/`bassSpelling` are optional enharmonic hints recorded by
|
|
51
|
+
* parsers (e.g. `parseChordSymbol('Bb7')`) so formatters can reproduce the
|
|
52
|
+
* original spelling instead of defaulting to sharps. Consumers may ignore
|
|
53
|
+
* them; a hint is only trusted when its pitch class still matches the
|
|
54
|
+
* corresponding `rootPc`/`bassPc`.
|
|
55
|
+
*
|
|
56
|
+
* @category Chords
|
|
57
|
+
*/
|
|
33
58
|
type Chord$1 = {
|
|
34
59
|
rootPc: number;
|
|
35
60
|
quality: ChordQuality;
|
|
36
61
|
intervals: number[];
|
|
37
62
|
bassPc?: number;
|
|
63
|
+
rootSpelling?: PitchSpelling;
|
|
64
|
+
bassSpelling?: PitchSpelling;
|
|
38
65
|
};
|
|
39
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* All chord qualities the builder understands, longest templates last.
|
|
68
|
+
*
|
|
69
|
+
* @category Chords
|
|
70
|
+
*/
|
|
40
71
|
declare function chordQualities(): ChordQuality[];
|
|
41
72
|
/**
|
|
42
73
|
* Build a chord rooted on a diatonic scale degree.
|
|
@@ -49,6 +80,8 @@ declare function chordQualities(): ChordQuality[];
|
|
|
49
80
|
* @param ext Chord quality to apply.
|
|
50
81
|
* @param key Key context supplying the diatonic root.
|
|
51
82
|
* @returns The constructed chord.
|
|
83
|
+
*
|
|
84
|
+
* @category Chords
|
|
52
85
|
*/
|
|
53
86
|
declare function chordFromDegree(degree: number, ext: ChordQuality, key: KeyScale): Chord$1;
|
|
54
87
|
/**
|
|
@@ -58,6 +91,15 @@ declare function chordFromDegree(degree: number, ext: ChordQuality, key: KeyScal
|
|
|
58
91
|
* @param quality Chord quality supplying the interval template.
|
|
59
92
|
* @param bassPc Optional slash-chord bass pitch class.
|
|
60
93
|
* @returns The constructed chord.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { makeChord } from '@libraz/libcantus';
|
|
98
|
+
* const cmaj7 = makeChord(0, 'maj7');
|
|
99
|
+
* // { rootPc: 0, quality: 'maj7', intervals: [0, 4, 7, 11] }
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @category Chords
|
|
61
103
|
*/
|
|
62
104
|
declare function makeChord(rootPc: number, quality: ChordQuality, bassPc?: number): Chord$1;
|
|
63
105
|
/**
|
|
@@ -65,6 +107,14 @@ declare function makeChord(rootPc: number, quality: ChordQuality, bassPc?: numbe
|
|
|
65
107
|
*
|
|
66
108
|
* @param chord The chord to enumerate.
|
|
67
109
|
* @returns The chord's pitch classes, sorted ascending in [0, 11].
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* import { makeChord, chordPitchClasses } from '@libraz/libcantus';
|
|
114
|
+
* chordPitchClasses(makeChord(0, 'maj7')); // [0, 4, 7, 11]
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @category Chords
|
|
68
118
|
*/
|
|
69
119
|
declare function chordPitchClasses(chord: Chord$1): number[];
|
|
70
120
|
/**
|
|
@@ -78,6 +128,8 @@ declare function chordPitchClasses(chord: Chord$1): number[];
|
|
|
78
128
|
* @param pitch MIDI pitch or bare pitch class.
|
|
79
129
|
* @param chord The chord providing the root reference.
|
|
80
130
|
* @returns The chord-tone role, or null if the pitch has no basic role.
|
|
131
|
+
*
|
|
132
|
+
* @category Chords
|
|
81
133
|
*/
|
|
82
134
|
declare function chordToneRole(pitch: number, chord: Chord$1): 'root' | 'third' | 'fifth' | 'sixth' | 'seventh' | null;
|
|
83
135
|
/**
|
|
@@ -91,6 +143,14 @@ declare function chordToneRole(pitch: number, chord: Chord$1): 'root' | 'third'
|
|
|
91
143
|
* @param degree 0-based scale degree of the chord root.
|
|
92
144
|
* @param key Key/scale context.
|
|
93
145
|
* @returns The diatonic triad.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { majorKey, diatonicTriad } from '@libraz/libcantus';
|
|
150
|
+
* const tonic = diatonicTriad(0, majorKey(0)); // C major triad (degree 0 of C major)
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @category Chords
|
|
94
154
|
*/
|
|
95
155
|
declare function diatonicTriad(degree: number, key: KeyScale): Chord$1;
|
|
96
156
|
/**
|
|
@@ -99,18 +159,32 @@ declare function diatonicTriad(degree: number, key: KeyScale): Chord$1;
|
|
|
99
159
|
* @param degree 0-based scale degree of the chord root.
|
|
100
160
|
* @param key Key/scale context.
|
|
101
161
|
* @returns The diatonic seventh chord.
|
|
162
|
+
*
|
|
163
|
+
* @category Chords
|
|
102
164
|
*/
|
|
103
165
|
declare function diatonicSeventh(degree: number, key: KeyScale): Chord$1;
|
|
104
166
|
|
|
105
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Severity policy: `strict` counterpoint vs. lenient `pop` voice-leading.
|
|
169
|
+
*
|
|
170
|
+
* @category Arrangement & Analysis
|
|
171
|
+
*/
|
|
106
172
|
type SafetyProfile = 'strict' | 'pop';
|
|
107
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* Overall placeability verdict for a candidate pitch.
|
|
175
|
+
*
|
|
176
|
+
* @category Arrangement & Analysis
|
|
177
|
+
*/
|
|
108
178
|
declare enum NoteSafety {
|
|
109
179
|
Safe = 0,
|
|
110
180
|
Warning = 1,
|
|
111
181
|
Dissonant = 2
|
|
112
182
|
}
|
|
113
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* Bit flags describing why a pitch received its verdict.
|
|
185
|
+
*
|
|
186
|
+
* @category Arrangement & Analysis
|
|
187
|
+
*/
|
|
114
188
|
declare enum ReasonFlag {
|
|
115
189
|
ChordTone = 1,
|
|
116
190
|
Tension = 2,
|
|
@@ -129,12 +203,20 @@ declare enum ReasonFlag {
|
|
|
129
203
|
Suspension = 16384,
|
|
130
204
|
NeedsResolution = 32768
|
|
131
205
|
}
|
|
132
|
-
/**
|
|
206
|
+
/**
|
|
207
|
+
* A neighbouring voice's current and previous pitch.
|
|
208
|
+
*
|
|
209
|
+
* @category Arrangement & Analysis
|
|
210
|
+
*/
|
|
133
211
|
type VoiceSnapshot = {
|
|
134
212
|
pitch: number;
|
|
135
213
|
prevPitch?: number;
|
|
136
214
|
};
|
|
137
|
-
/**
|
|
215
|
+
/**
|
|
216
|
+
* Everything needed to evaluate one candidate pitch.
|
|
217
|
+
*
|
|
218
|
+
* @category Arrangement & Analysis
|
|
219
|
+
*/
|
|
138
220
|
type SafetyQuery = {
|
|
139
221
|
profile: SafetyProfile;
|
|
140
222
|
candidatePitch: number;
|
|
@@ -146,7 +228,11 @@ type SafetyQuery = {
|
|
|
146
228
|
vocalLow?: number;
|
|
147
229
|
vocalHigh?: number;
|
|
148
230
|
};
|
|
149
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* The verdict, reason flags, and optional resolution guidance.
|
|
233
|
+
*
|
|
234
|
+
* @category Arrangement & Analysis
|
|
235
|
+
*/
|
|
150
236
|
type SafetyResult = {
|
|
151
237
|
safety: NoteSafety;
|
|
152
238
|
reasons: number;
|
|
@@ -164,6 +250,20 @@ type SafetyResult = {
|
|
|
164
250
|
*
|
|
165
251
|
* @param q The candidate and its harmonic/voice-leading context.
|
|
166
252
|
* @returns The verdict, reason bitmask, and optional resolution guidance.
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* import { evaluateSafety, makeChord, majorKey, NoteSafety } from '@libraz/libcantus';
|
|
256
|
+
* const result = evaluateSafety({
|
|
257
|
+
* profile: 'pop',
|
|
258
|
+
* candidatePitch: 60, // C over a C major chord
|
|
259
|
+
* chord: makeChord(0, 'maj'),
|
|
260
|
+
* key: majorKey(0),
|
|
261
|
+
* otherVoices: [],
|
|
262
|
+
* strongBeat: true,
|
|
263
|
+
* });
|
|
264
|
+
* result.safety === NoteSafety.Safe; // true — C is a chord tone
|
|
265
|
+
* ```
|
|
266
|
+
* @category Arrangement & Analysis
|
|
167
267
|
*/
|
|
168
268
|
declare function evaluateSafety(q: SafetyQuery): SafetyResult;
|
|
169
269
|
/**
|
|
@@ -173,10 +273,25 @@ declare function evaluateSafety(q: SafetyQuery): SafetyResult;
|
|
|
173
273
|
* @param pitchLow Lowest MIDI pitch to consider (inclusive).
|
|
174
274
|
* @param pitchHigh Highest MIDI pitch to consider (inclusive).
|
|
175
275
|
* @returns Placeable pitches (non-dissonant), chord tones before others, each group descending.
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* import { enumerateSafePitches, makeChord, majorKey } from '@libraz/libcantus';
|
|
279
|
+
* const pitches = enumerateSafePitches(
|
|
280
|
+
* { profile: 'pop', chord: makeChord(0, 'maj'), key: majorKey(0), otherVoices: [], strongBeat: true },
|
|
281
|
+
* 60,
|
|
282
|
+
* 72,
|
|
283
|
+
* );
|
|
284
|
+
* pitches; // placeable pitches in [60, 72], chord tones first, each group descending
|
|
285
|
+
* ```
|
|
286
|
+
* @category Arrangement & Analysis
|
|
176
287
|
*/
|
|
177
288
|
declare function enumerateSafePitches(q: Omit<SafetyQuery, 'candidatePitch'>, pitchLow: number, pitchHigh: number): number[];
|
|
178
289
|
|
|
179
|
-
/**
|
|
290
|
+
/**
|
|
291
|
+
* A theory annotation attached to a note.
|
|
292
|
+
*
|
|
293
|
+
* @category Arrangement & Analysis
|
|
294
|
+
*/
|
|
180
295
|
type TheoryLabel = {
|
|
181
296
|
kind: 'chordTone';
|
|
182
297
|
role: 'root' | 'third' | 'fifth' | 'sixth' | 'seventh';
|
|
@@ -204,13 +319,21 @@ type TheoryLabel = {
|
|
|
204
319
|
kind: 'leadingTone';
|
|
205
320
|
resolveTo: number;
|
|
206
321
|
};
|
|
207
|
-
/**
|
|
322
|
+
/**
|
|
323
|
+
* A note with its theory labels and a short rationale.
|
|
324
|
+
*
|
|
325
|
+
* @category Arrangement & Analysis
|
|
326
|
+
*/
|
|
208
327
|
type AnalyzedNote = {
|
|
209
328
|
noteId: number;
|
|
210
329
|
labels: TheoryLabel[];
|
|
211
330
|
rationale?: string;
|
|
212
331
|
};
|
|
213
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* A single note in a monophonic voice: a {@link NoteEvent} with a stable id.
|
|
334
|
+
*
|
|
335
|
+
* @category Arrangement & Analysis
|
|
336
|
+
*/
|
|
214
337
|
type VoiceNote = NoteEvent & {
|
|
215
338
|
id: number;
|
|
216
339
|
};
|
|
@@ -224,11 +347,30 @@ type VoiceNote = NoteEvent & {
|
|
|
224
347
|
* avoid, or an unresolved-dissonance label. Leading-tone resolutions are noted
|
|
225
348
|
* additionally.
|
|
226
349
|
*
|
|
350
|
+
* The voice is expected to be monophonic (one note at a time). Notes sharing an
|
|
351
|
+
* onset are treated as simultaneous cluster members, not melodic neighbors, so
|
|
352
|
+
* they receive no melodic labels (suspension, passing, neighbor, anticipation,
|
|
353
|
+
* escape) — only harmonic ones. Callers with truly polyphonic material should
|
|
354
|
+
* split it into monophonic sub-voices first, as `analyzeArrangement` does.
|
|
355
|
+
*
|
|
227
356
|
* @param voice The monophonic voice, in time order.
|
|
228
357
|
* @param chordAtBeat Chord sounding at a given beat, or null.
|
|
229
358
|
* @param key Key context for leading-tone detection.
|
|
230
359
|
* @param otherVoicesAtBeat Other sounding voices at a given beat.
|
|
231
360
|
* @returns One annotation per input note.
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* import { analyzeVoice, makeChord, majorKey } from '@libraz/libcantus';
|
|
364
|
+
* const voice = [
|
|
365
|
+
* { id: 0, pitch: 60, startBeat: 0, durationBeat: 1 },
|
|
366
|
+
* { id: 1, pitch: 62, startBeat: 1, durationBeat: 1 },
|
|
367
|
+
* { id: 2, pitch: 64, startBeat: 2, durationBeat: 1 },
|
|
368
|
+
* ];
|
|
369
|
+
* const cMajor = makeChord(0, 'maj');
|
|
370
|
+
* const labels = analyzeVoice(voice, () => cMajor, majorKey(0), () => []);
|
|
371
|
+
* labels; // one AnalyzedNote per input note, in the same order
|
|
372
|
+
* ```
|
|
373
|
+
* @category Arrangement & Analysis
|
|
232
374
|
*/
|
|
233
375
|
declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) => Chord$1 | null, key: KeyScale, otherVoicesAtBeat: (beat: number) => VoiceSnapshot[]): AnalyzedNote[];
|
|
234
376
|
|
|
@@ -239,12 +381,30 @@ declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) =>
|
|
|
239
381
|
* Positions and durations are measured in quarter-note beats, matching the rest
|
|
240
382
|
* of the library's beat convention (four quarter-note beats per bar in 4/4).
|
|
241
383
|
*/
|
|
242
|
-
/**
|
|
384
|
+
/**
|
|
385
|
+
* A time signature as a numerator over a note-value denominator.
|
|
386
|
+
*
|
|
387
|
+
* @category Rhythm & Meter
|
|
388
|
+
*/
|
|
243
389
|
type TimeSignature = {
|
|
244
390
|
numerator: number;
|
|
245
391
|
denominator: number;
|
|
392
|
+
/**
|
|
393
|
+
* Optional additive grouping of the bar's main pulses, as the felt-beat
|
|
394
|
+
* lengths in pulses — e.g. `[2, 2, 3]` for a 2+2+3 reading of 7/8, or
|
|
395
|
+
* `[3, 2]` for 5/8. The entries must be positive integers summing to
|
|
396
|
+
* {@link pulsesPerBar}. When present, the head pulse of each group (other
|
|
397
|
+
* than the downbeat) is treated as a secondary strong pulse by
|
|
398
|
+
* {@link metricWeight}/{@link isStrongBeat}; when absent, all main pulses
|
|
399
|
+
* are weighted equally as flat, evenly divided pulses.
|
|
400
|
+
*/
|
|
401
|
+
grouping?: number[];
|
|
246
402
|
};
|
|
247
|
-
/**
|
|
403
|
+
/**
|
|
404
|
+
* A position expressed as a bar index and a quarter-note offset within the bar.
|
|
405
|
+
*
|
|
406
|
+
* @category Rhythm & Meter
|
|
407
|
+
*/
|
|
248
408
|
type BarPosition = {
|
|
249
409
|
bar: number;
|
|
250
410
|
/** Quarter-note offset from the start of the bar. */
|
|
@@ -256,9 +416,19 @@ type BarPosition = {
|
|
|
256
416
|
* @param text The signature text.
|
|
257
417
|
* @returns The parsed time signature.
|
|
258
418
|
* @throws If the text is not `n/d` with positive integers.
|
|
419
|
+
* @example
|
|
420
|
+
* ```ts
|
|
421
|
+
* import { parseTimeSignature } from '@libraz/libcantus';
|
|
422
|
+
* parseTimeSignature('6/8'); // { numerator: 6, denominator: 8 }
|
|
423
|
+
* ```
|
|
424
|
+
* @category Rhythm & Meter
|
|
259
425
|
*/
|
|
260
426
|
declare function parseTimeSignature(text: string): TimeSignature;
|
|
261
|
-
/**
|
|
427
|
+
/**
|
|
428
|
+
* Render a time signature as `"n/d"`.
|
|
429
|
+
*
|
|
430
|
+
* @category Rhythm & Meter
|
|
431
|
+
*/
|
|
262
432
|
declare function formatTimeSignature(ts: TimeSignature): string;
|
|
263
433
|
/**
|
|
264
434
|
* Whether a meter is compound: its main pulses each divide into three, as in
|
|
@@ -268,6 +438,7 @@ declare function formatTimeSignature(ts: TimeSignature): string;
|
|
|
268
438
|
*
|
|
269
439
|
* @param ts The time signature.
|
|
270
440
|
* @returns True for compound meters.
|
|
441
|
+
* @category Rhythm & Meter
|
|
271
442
|
*/
|
|
272
443
|
declare function isCompound(ts: TimeSignature): boolean;
|
|
273
444
|
/**
|
|
@@ -275,14 +446,22 @@ declare function isCompound(ts: TimeSignature): boolean;
|
|
|
275
446
|
*
|
|
276
447
|
* @param ts The time signature.
|
|
277
448
|
* @returns The bar length in quarter notes.
|
|
449
|
+
* @category Rhythm & Meter
|
|
278
450
|
*/
|
|
279
451
|
declare function beatsPerBar(ts: TimeSignature): number;
|
|
280
452
|
/**
|
|
281
453
|
* Number of main pulses (felt beats) per bar: the numerator for simple meters,
|
|
282
454
|
* a third of it for compound meters.
|
|
283
455
|
*
|
|
456
|
+
* For additive/irregular meters such as 7/8 or 5/8 this counts the raw pulses
|
|
457
|
+
* (7 and 5 respectively), which are otherwise felt as flat, evenly divided and
|
|
458
|
+
* equally accented pulses. To recover a 2+2+3 or 3+2 felt-beat grouping, set
|
|
459
|
+
* {@link TimeSignature.grouping}; {@link metricWeight} then accents each
|
|
460
|
+
* group's head pulse.
|
|
461
|
+
*
|
|
284
462
|
* @param ts The time signature.
|
|
285
463
|
* @returns The pulse count per bar.
|
|
464
|
+
* @category Rhythm & Meter
|
|
286
465
|
*/
|
|
287
466
|
declare function pulsesPerBar(ts: TimeSignature): number;
|
|
288
467
|
/**
|
|
@@ -291,6 +470,13 @@ declare function pulsesPerBar(ts: TimeSignature): number;
|
|
|
291
470
|
* @param beatInQuarters Absolute position in quarter-note beats.
|
|
292
471
|
* @param ts The time signature.
|
|
293
472
|
* @returns The bar and in-bar quarter-note offset.
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* import { parseTimeSignature, beatToBarPosition } from '@libraz/libcantus';
|
|
476
|
+
* const ts = parseTimeSignature('4/4');
|
|
477
|
+
* beatToBarPosition(5, ts); // { bar: 1, beat: 1 }
|
|
478
|
+
* ```
|
|
479
|
+
* @category Rhythm & Meter
|
|
294
480
|
*/
|
|
295
481
|
declare function beatToBarPosition(beatInQuarters: number, ts: TimeSignature): BarPosition;
|
|
296
482
|
/**
|
|
@@ -300,16 +486,33 @@ declare function beatToBarPosition(beatInQuarters: number, ts: TimeSignature): B
|
|
|
300
486
|
* @param pos The bar position.
|
|
301
487
|
* @param ts The time signature.
|
|
302
488
|
* @returns The absolute position in quarter-note beats.
|
|
489
|
+
* @category Rhythm & Meter
|
|
303
490
|
*/
|
|
304
491
|
declare function barPositionToBeat(pos: BarPosition, ts: TimeSignature): number;
|
|
305
492
|
/**
|
|
306
493
|
* Metric weight of a position within its bar, on a 0–3 scale:
|
|
307
|
-
* 3 the downbeat, 2 a secondary strong pulse
|
|
308
|
-
*
|
|
494
|
+
* 3 the downbeat, 2 a secondary strong pulse, 1 any other main pulse, and 0 an
|
|
495
|
+
* off-pulse subdivision.
|
|
496
|
+
*
|
|
497
|
+
* The secondary strong pulse is the bar's midpoint in even simple/compound
|
|
498
|
+
* meters. For an additive/irregular meter with {@link TimeSignature.grouping}
|
|
499
|
+
* set (e.g. `[2, 2, 3]` for 7/8), each group's head pulse other than the
|
|
500
|
+
* downbeat is the secondary strong pulse instead; without a grouping every
|
|
501
|
+
* non-downbeat main pulse of such a meter weighs 1 (flat, evenly divided
|
|
502
|
+
* pulses).
|
|
309
503
|
*
|
|
310
504
|
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
311
505
|
* @param ts The time signature.
|
|
312
506
|
* @returns The metric weight (0–3).
|
|
507
|
+
* @throws If `ts.grouping` is present but is not a positive-integer list
|
|
508
|
+
* summing to {@link pulsesPerBar}.
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* import { parseTimeSignature, metricWeight } from '@libraz/libcantus';
|
|
512
|
+
* const ts = parseTimeSignature('4/4');
|
|
513
|
+
* metricWeight(0, ts); // 3 (the downbeat)
|
|
514
|
+
* ```
|
|
515
|
+
* @category Rhythm & Meter
|
|
313
516
|
*/
|
|
314
517
|
declare function metricWeight(beatInQuarters: number, ts: TimeSignature): number;
|
|
315
518
|
/**
|
|
@@ -319,6 +522,7 @@ declare function metricWeight(beatInQuarters: number, ts: TimeSignature): number
|
|
|
319
522
|
* @param beatInQuarters Absolute or in-bar quarter-note position.
|
|
320
523
|
* @param ts The time signature.
|
|
321
524
|
* @returns True on strong beats.
|
|
525
|
+
* @category Rhythm & Meter
|
|
322
526
|
*/
|
|
323
527
|
declare function isStrongBeat(beatInQuarters: number, ts: TimeSignature): boolean;
|
|
324
528
|
/**
|
|
@@ -329,6 +533,7 @@ declare function isStrongBeat(beatInQuarters: number, ts: TimeSignature): boolea
|
|
|
329
533
|
* @param count Number of equal parts.
|
|
330
534
|
* @returns `count` equal durations summing to `totalBeats`.
|
|
331
535
|
* @throws If `count` is not a positive integer.
|
|
536
|
+
* @category Rhythm & Meter
|
|
332
537
|
*/
|
|
333
538
|
declare function tuplet(totalBeats: number, count: number): number[];
|
|
334
539
|
|
|
@@ -341,11 +546,23 @@ declare function tuplet(totalBeats: number, count: number): number[];
|
|
|
341
546
|
* spelled key signature.
|
|
342
547
|
*/
|
|
343
548
|
|
|
344
|
-
/**
|
|
549
|
+
/**
|
|
550
|
+
* The three broad harmonic functions of tonal music.
|
|
551
|
+
*
|
|
552
|
+
* @category Functional Harmony
|
|
553
|
+
*/
|
|
345
554
|
type HarmonicFunction = 'tonic' | 'subdominant' | 'dominant';
|
|
346
|
-
/**
|
|
555
|
+
/**
|
|
556
|
+
* A recognized cadence type, or null when a chord pair forms none.
|
|
557
|
+
*
|
|
558
|
+
* @category Functional Harmony
|
|
559
|
+
*/
|
|
347
560
|
type Cadence = 'authentic' | 'plagal' | 'half' | 'deceptive' | null;
|
|
348
|
-
/**
|
|
561
|
+
/**
|
|
562
|
+
* Whether a key's scale has a minor third and no major third (a minor key).
|
|
563
|
+
*
|
|
564
|
+
* @category Functional Harmony
|
|
565
|
+
*/
|
|
349
566
|
declare function isMinorKey(key: KeyScale): boolean;
|
|
350
567
|
/**
|
|
351
568
|
* Build the chord denoted by a Roman numeral in a key.
|
|
@@ -358,14 +575,24 @@ declare function isMinorKey(key: KeyScale): boolean;
|
|
|
358
575
|
* carry a `bassPc`.
|
|
359
576
|
*
|
|
360
577
|
* The `ø` glyph always denotes the half-diminished seventh (`iiø` == `iiø7` ==
|
|
361
|
-
* `m7b5`), since no half-diminished triad exists.
|
|
362
|
-
*
|
|
578
|
+
* `m7b5`), since no half-diminished triad exists. Extension and added-tone
|
|
579
|
+
* suffixes are the canonical case-sensitive forms emitted by
|
|
580
|
+
* {@link chordToRoman} and honor the numeral case: `V9` -> `dom9`, `ii9` ->
|
|
581
|
+
* `min9`, `Imaj9` -> `maj9`, `V11` -> `11`, `V13` -> `13`, `Iadd6`/`iadd6` ->
|
|
582
|
+
* `6`/`min6`, `I69` -> `6/9`, `Isus2`/`Isus4`, `Iadd9`/`Iadd11`, `Ib5` ->
|
|
583
|
+
* `majb5`, `I5` -> `5`, and the altered dominants `V7b9`/`V7#9`/`V7#11`/`V7b13`.
|
|
363
584
|
* A figure string that is neither a recognized inversion nor a supported
|
|
364
|
-
*
|
|
585
|
+
* quality suffix throws rather than silently degrading to a triad.
|
|
365
586
|
*
|
|
366
587
|
* @param text The Roman numeral.
|
|
367
588
|
* @param key The prevailing key.
|
|
368
589
|
* @returns The chord.
|
|
590
|
+
* @example
|
|
591
|
+
* ```ts
|
|
592
|
+
* import { romanToChord, majorKey } from '@libraz/libcantus';
|
|
593
|
+
* romanToChord('V7', majorKey(0)); // G7 in C major: { rootPc: 7, quality: 'dom7', ... }
|
|
594
|
+
* ```
|
|
595
|
+
* @category Functional Harmony
|
|
369
596
|
*/
|
|
370
597
|
declare function romanToChord(text: string, key: KeyScale): Chord$1;
|
|
371
598
|
/**
|
|
@@ -375,11 +602,20 @@ declare function romanToChord(text: string, key: KeyScale): Chord$1;
|
|
|
375
602
|
* correct in both major and minor keys (and any custom scale). The quality
|
|
376
603
|
* selects the case and suffix; chromatic roots receive a flat/sharp spelling by
|
|
377
604
|
* convention. When the chord carries a `bassPc` on a chord tone, a figured-bass
|
|
378
|
-
* inversion (`6`, `64`, `65`, `43`, `42`) is emitted
|
|
605
|
+
* inversion (`6`, `64`, `65`, `43`, `42`) is emitted for plain triads and true
|
|
606
|
+
* seventh chords; added-tone and extended qualities have no lossless figure and
|
|
607
|
+
* render in root position instead (the bass is dropped, pitch classes are
|
|
608
|
+
* preserved).
|
|
379
609
|
*
|
|
380
610
|
* @param chord The chord to name.
|
|
381
611
|
* @param key The prevailing key.
|
|
382
612
|
* @returns The Roman numeral string.
|
|
613
|
+
* @example
|
|
614
|
+
* ```ts
|
|
615
|
+
* import { chordToRoman, makeChord, majorKey } from '@libraz/libcantus';
|
|
616
|
+
* chordToRoman(makeChord(7, 'dom7'), majorKey(0)); // => 'V7' (G7 in C major)
|
|
617
|
+
* ```
|
|
618
|
+
* @category Functional Harmony
|
|
383
619
|
*/
|
|
384
620
|
declare function chordToRoman(chord: Chord$1, key: KeyScale): string;
|
|
385
621
|
/**
|
|
@@ -390,11 +626,20 @@ declare function chordToRoman(chord: Chord$1, key: KeyScale): string;
|
|
|
390
626
|
* @param chord The chord.
|
|
391
627
|
* @param key The prevailing key.
|
|
392
628
|
* @returns The harmonic function.
|
|
629
|
+
* @category Functional Harmony
|
|
393
630
|
*/
|
|
394
631
|
declare function functionOf(chord: Chord$1, key: KeyScale): HarmonicFunction;
|
|
395
|
-
/**
|
|
632
|
+
/**
|
|
633
|
+
* The origin of a recognized non-diatonic chord, or null when none applies.
|
|
634
|
+
*
|
|
635
|
+
* @category Functional Harmony
|
|
636
|
+
*/
|
|
396
637
|
type BorrowedSource = 'parallel-minor' | 'parallel-major' | 'neapolitan' | null;
|
|
397
|
-
/**
|
|
638
|
+
/**
|
|
639
|
+
* The result of {@link analyzeChord}: function, borrowing, and Roman numeral.
|
|
640
|
+
*
|
|
641
|
+
* @category Functional Harmony
|
|
642
|
+
*/
|
|
398
643
|
type ChordAnalysis = {
|
|
399
644
|
function: HarmonicFunction;
|
|
400
645
|
borrowed: boolean;
|
|
@@ -412,6 +657,7 @@ type ChordAnalysis = {
|
|
|
412
657
|
* @param chord The chord to test.
|
|
413
658
|
* @param key The prevailing key.
|
|
414
659
|
* @returns True if all chord pitch classes are scale tones.
|
|
660
|
+
* @category Functional Harmony
|
|
415
661
|
*/
|
|
416
662
|
declare function isDiatonic(chord: Chord$1, key: KeyScale): boolean;
|
|
417
663
|
/**
|
|
@@ -422,6 +668,7 @@ declare function isDiatonic(chord: Chord$1, key: KeyScale): boolean;
|
|
|
422
668
|
*
|
|
423
669
|
* @param key The key to mirror.
|
|
424
670
|
* @returns The parallel major or natural-minor key on the same tonic.
|
|
671
|
+
* @category Functional Harmony
|
|
425
672
|
*/
|
|
426
673
|
declare function parallelKey(key: KeyScale): KeyScale;
|
|
427
674
|
/**
|
|
@@ -438,6 +685,7 @@ declare function parallelKey(key: KeyScale): KeyScale;
|
|
|
438
685
|
* @param chord The chord to test.
|
|
439
686
|
* @param key The prevailing key.
|
|
440
687
|
* @returns True if the chord is borrowed from the parallel mode.
|
|
688
|
+
* @category Functional Harmony
|
|
441
689
|
*/
|
|
442
690
|
declare function isBorrowedChord(chord: Chord$1, key: KeyScale): boolean;
|
|
443
691
|
/**
|
|
@@ -451,6 +699,7 @@ declare function isBorrowedChord(chord: Chord$1, key: KeyScale): boolean;
|
|
|
451
699
|
* @param chord The chord to classify.
|
|
452
700
|
* @param key The prevailing key.
|
|
453
701
|
* @returns The borrowing source, or null.
|
|
702
|
+
* @category Functional Harmony
|
|
454
703
|
*/
|
|
455
704
|
declare function borrowedSource(chord: Chord$1, key: KeyScale): BorrowedSource;
|
|
456
705
|
/**
|
|
@@ -465,6 +714,13 @@ declare function borrowedSource(chord: Chord$1, key: KeyScale): BorrowedSource;
|
|
|
465
714
|
* @param chord The chord to analyze.
|
|
466
715
|
* @param key The prevailing key.
|
|
467
716
|
* @returns The chord analysis.
|
|
717
|
+
* @example
|
|
718
|
+
* ```ts
|
|
719
|
+
* import { analyzeChord, makeChord, majorKey } from '@libraz/libcantus';
|
|
720
|
+
* analyzeChord(makeChord(7, 'dom7'), majorKey(0));
|
|
721
|
+
* // { function: 'dominant', borrowed: false, source: null, roman: 'V7' }
|
|
722
|
+
* ```
|
|
723
|
+
* @category Functional Harmony
|
|
468
724
|
*/
|
|
469
725
|
declare function analyzeChord(chord: Chord$1, key: KeyScale): ChordAnalysis;
|
|
470
726
|
/**
|
|
@@ -482,6 +738,12 @@ declare function analyzeChord(chord: Chord$1, key: KeyScale): ChordAnalysis;
|
|
|
482
738
|
* @param to The final chord.
|
|
483
739
|
* @param key The prevailing key.
|
|
484
740
|
* @returns The cadence type, or null.
|
|
741
|
+
* @example
|
|
742
|
+
* ```ts
|
|
743
|
+
* import { detectCadence, makeChord, majorKey } from '@libraz/libcantus';
|
|
744
|
+
* detectCadence(makeChord(7, 'maj'), makeChord(0, 'maj'), majorKey(0)); // => 'authentic'
|
|
745
|
+
* ```
|
|
746
|
+
* @category Functional Harmony
|
|
485
747
|
*/
|
|
486
748
|
declare function detectCadence(from: Chord$1, to: Chord$1, key: KeyScale): Cadence;
|
|
487
749
|
/**
|
|
@@ -490,14 +752,27 @@ declare function detectCadence(from: Chord$1, to: Chord$1, key: KeyScale): Caden
|
|
|
490
752
|
* @param targetDegree 0-based scale degree to tonicize.
|
|
491
753
|
* @param key The prevailing key.
|
|
492
754
|
* @returns A dominant-seventh chord a fifth above the target's root.
|
|
755
|
+
* @category Functional Harmony
|
|
493
756
|
*/
|
|
494
757
|
declare function secondaryDominant(targetDegree: number, key: KeyScale): Chord$1;
|
|
495
758
|
|
|
496
|
-
/**
|
|
759
|
+
/**
|
|
760
|
+
* Broad production style a progression preset suits.
|
|
761
|
+
*
|
|
762
|
+
* @category Composition
|
|
763
|
+
*/
|
|
497
764
|
type ProgStyle = 'minimal' | 'dance' | 'idol' | 'rock';
|
|
498
|
-
/**
|
|
765
|
+
/**
|
|
766
|
+
* Harmonic-function role of a progression as a whole.
|
|
767
|
+
*
|
|
768
|
+
* @category Composition
|
|
769
|
+
*/
|
|
499
770
|
type ProgFunction = 'loop' | 'tensionBuild' | 'cadenceStrong' | 'stable';
|
|
500
|
-
/**
|
|
771
|
+
/**
|
|
772
|
+
* A named chord-progression preset expressed in scale degrees.
|
|
773
|
+
*
|
|
774
|
+
* @category Composition
|
|
775
|
+
*/
|
|
501
776
|
type ProgressionPreset = {
|
|
502
777
|
id: string;
|
|
503
778
|
name: string;
|
|
@@ -505,7 +780,11 @@ type ProgressionPreset = {
|
|
|
505
780
|
functional: ProgFunction;
|
|
506
781
|
styles: ProgStyle[];
|
|
507
782
|
};
|
|
508
|
-
/**
|
|
783
|
+
/**
|
|
784
|
+
* A chord placed on the timeline by {@link generateProgression}.
|
|
785
|
+
*
|
|
786
|
+
* @category Composition
|
|
787
|
+
*/
|
|
509
788
|
type GeneratedChord = {
|
|
510
789
|
rootPc: number;
|
|
511
790
|
quality: ChordQuality;
|
|
@@ -516,7 +795,11 @@ type GeneratedChord = {
|
|
|
516
795
|
/** True when the chord is a secondary dominant tonicizing another degree. */
|
|
517
796
|
secondaryDominant?: boolean;
|
|
518
797
|
};
|
|
519
|
-
/**
|
|
798
|
+
/**
|
|
799
|
+
* Options controlling {@link generateProgression}.
|
|
800
|
+
*
|
|
801
|
+
* @category Composition
|
|
802
|
+
*/
|
|
520
803
|
type GenerateProgressionOptions = {
|
|
521
804
|
key: KeyScale;
|
|
522
805
|
style: ProgStyle;
|
|
@@ -524,17 +807,32 @@ type GenerateProgressionOptions = {
|
|
|
524
807
|
presetId?: string;
|
|
525
808
|
ext?: ChordQuality | 'auto';
|
|
526
809
|
reharmonize?: boolean;
|
|
810
|
+
/**
|
|
811
|
+
* Seed for the deterministic preset choice and reharmonization.
|
|
812
|
+
*
|
|
813
|
+
* @defaultValue 0
|
|
814
|
+
*/
|
|
527
815
|
seed?: number;
|
|
528
816
|
};
|
|
529
|
-
/**
|
|
817
|
+
/**
|
|
818
|
+
* All built-in progression presets.
|
|
819
|
+
*
|
|
820
|
+
* @category Composition
|
|
821
|
+
*/
|
|
530
822
|
declare function progressions(): ProgressionPreset[];
|
|
531
|
-
/**
|
|
823
|
+
/**
|
|
824
|
+
* Presets whose style list includes the given style.
|
|
825
|
+
*
|
|
826
|
+
* @category Composition
|
|
827
|
+
*/
|
|
532
828
|
declare function progressionsByStyle(style: ProgStyle): ProgressionPreset[];
|
|
533
829
|
/**
|
|
534
830
|
* Generate a chord progression laid out one chord per bar.
|
|
535
831
|
*
|
|
536
832
|
* A preset is chosen by `presetId` when given, otherwise deterministically from
|
|
537
|
-
* the presets matching `style`, seeded by `seed`.
|
|
833
|
+
* the presets matching `style`, seeded by `seed`. An unknown `presetId` is a
|
|
834
|
+
* caller error and throws rather than silently falling back to a random preset.
|
|
835
|
+
* The preset's degrees cycle to
|
|
538
836
|
* fill `bars`; each bar is four beats, so `startBeat` is `barIndex * 4`. Chord
|
|
539
837
|
* roots come from the key's diatonic scale-degree mapping. When `ext` is
|
|
540
838
|
* omitted or `'auto'`, each chord takes its diatonic triad quality; otherwise
|
|
@@ -544,16 +842,34 @@ declare function progressionsByStyle(style: ProgStyle): ProgressionPreset[];
|
|
|
544
842
|
*
|
|
545
843
|
* @param opts Generation options.
|
|
546
844
|
* @returns One chord per bar in timeline order.
|
|
845
|
+
* @throws If `presetId` is given but does not match any built-in preset.
|
|
846
|
+
*
|
|
847
|
+
* @example
|
|
848
|
+
* ```ts
|
|
849
|
+
* import { generateProgression, majorKey } from '@libraz/libcantus';
|
|
850
|
+
* const chords = generateProgression({ key: majorKey(0), style: 'dance', bars: 4 });
|
|
851
|
+
* // Deterministic for a given seed (defaults to 0); one GeneratedChord per bar.
|
|
852
|
+
* ```
|
|
853
|
+
*
|
|
854
|
+
* @category Composition
|
|
547
855
|
*/
|
|
548
856
|
declare function generateProgression(opts: GenerateProgressionOptions): GeneratedChord[];
|
|
549
857
|
|
|
550
|
-
/**
|
|
858
|
+
/**
|
|
859
|
+
* A chord occupying a half-open beat span.
|
|
860
|
+
*
|
|
861
|
+
* @category Arrangement & Analysis
|
|
862
|
+
*/
|
|
551
863
|
type ChordSegment = {
|
|
552
864
|
startBeat: number;
|
|
553
865
|
endBeat: number;
|
|
554
866
|
chord: Chord$1;
|
|
555
867
|
};
|
|
556
|
-
/**
|
|
868
|
+
/**
|
|
869
|
+
* A beat-indexed sequence of chord segments.
|
|
870
|
+
*
|
|
871
|
+
* @category Arrangement & Analysis
|
|
872
|
+
*/
|
|
557
873
|
type ChordTimeline = {
|
|
558
874
|
/** The chord sounding at a beat, or null when no segment covers it. */
|
|
559
875
|
at: (beat: number) => Chord$1 | null;
|
|
@@ -570,20 +886,41 @@ type ChordTimeline = {
|
|
|
570
886
|
* @param chords Placed chords in time order.
|
|
571
887
|
* @param totalBeats End of the timeline in beats.
|
|
572
888
|
* @returns A queryable chord timeline.
|
|
889
|
+
* @category Arrangement & Analysis
|
|
573
890
|
*/
|
|
574
891
|
declare function chordTimelineFromChords(chords: GeneratedChord[], totalBeats: number): ChordTimeline;
|
|
575
|
-
/**
|
|
892
|
+
/**
|
|
893
|
+
* Options controlling {@link chordTimelineFromNotes}.
|
|
894
|
+
*
|
|
895
|
+
* @category Arrangement & Analysis
|
|
896
|
+
*/
|
|
576
897
|
type ChordTimelineOptions = {
|
|
577
898
|
/** Key context; inferred from the notes with {@link detectKey} when omitted. */
|
|
578
899
|
key?: KeyScale;
|
|
579
|
-
/**
|
|
900
|
+
/**
|
|
901
|
+
* Time signature used for metric accents; defaults to 4/4.
|
|
902
|
+
*
|
|
903
|
+
* @defaultValue `4/4`
|
|
904
|
+
*/
|
|
580
905
|
ts?: TimeSignature;
|
|
581
|
-
/**
|
|
906
|
+
/**
|
|
907
|
+
* Window length in beats per chord slot; defaults to one bar of `ts`.
|
|
908
|
+
*
|
|
909
|
+
* @defaultValue one bar of `ts`
|
|
910
|
+
*/
|
|
582
911
|
harmonicRhythm?: number;
|
|
583
|
-
/**
|
|
912
|
+
/**
|
|
913
|
+
* End of the analyzed span in beats; defaults to the end of the last note.
|
|
914
|
+
*
|
|
915
|
+
* @defaultValue the end of the last note
|
|
916
|
+
*/
|
|
584
917
|
totalBeats?: number;
|
|
585
918
|
};
|
|
586
|
-
/**
|
|
919
|
+
/**
|
|
920
|
+
* The result of {@link chordTimelineFromNotes}.
|
|
921
|
+
*
|
|
922
|
+
* @category Arrangement & Analysis
|
|
923
|
+
*/
|
|
587
924
|
type ChordTimelineResult = {
|
|
588
925
|
/** The inferred timeline, with adjacent identical chords merged. */
|
|
589
926
|
timeline: ChordTimeline;
|
|
@@ -604,13 +941,33 @@ type ChordTimelineResult = {
|
|
|
604
941
|
* the fraction of the window weight explained by chord tones, reduced when the
|
|
605
942
|
* match is inexact, and duration-weighted across merged windows.
|
|
606
943
|
*
|
|
944
|
+
* Notes with a zero or negative duration never sound, so they are dropped at
|
|
945
|
+
* ingest: they contribute to neither the key inference, the span, nor any
|
|
946
|
+
* window's histogram.
|
|
947
|
+
*
|
|
607
948
|
* @param notes The notes to analyze (any number of tracks, flattened).
|
|
608
949
|
* @param opts Analysis options; see {@link ChordTimelineOptions}.
|
|
609
950
|
* @returns The inferred timeline, the key used, and per-segment confidences.
|
|
610
951
|
* @throws If `harmonicRhythm` is not positive.
|
|
952
|
+
* @example
|
|
953
|
+
* ```ts
|
|
954
|
+
* import { chordTimelineFromNotes } from '@libraz/libcantus';
|
|
955
|
+
* const notes = [
|
|
956
|
+
* { pitch: 60, startBeat: 0, durationBeat: 2 }, // C
|
|
957
|
+
* { pitch: 64, startBeat: 0, durationBeat: 2 }, // E
|
|
958
|
+
* { pitch: 67, startBeat: 0, durationBeat: 2 }, // G
|
|
959
|
+
* ];
|
|
960
|
+
* const { timeline, key } = chordTimelineFromNotes(notes);
|
|
961
|
+
* timeline.at(0); // the chord inferred over beat 0, or null
|
|
962
|
+
* ```
|
|
963
|
+
* @category Arrangement & Analysis
|
|
611
964
|
*/
|
|
612
965
|
declare function chordTimelineFromNotes(notes: NoteEvent[], opts?: ChordTimelineOptions): ChordTimelineResult;
|
|
613
|
-
/**
|
|
966
|
+
/**
|
|
967
|
+
* A cadence found between two consecutive timeline segments.
|
|
968
|
+
*
|
|
969
|
+
* @category Arrangement & Analysis
|
|
970
|
+
*/
|
|
614
971
|
type CadenceHit = {
|
|
615
972
|
/** The beat where the cadence arrives (the second chord's onset). */
|
|
616
973
|
atBeat: number;
|
|
@@ -621,12 +978,25 @@ type CadenceHit = {
|
|
|
621
978
|
/**
|
|
622
979
|
* Find cadences between consecutive segments of a chord timeline.
|
|
623
980
|
*
|
|
624
|
-
* Each adjacent segment pair is classified with
|
|
625
|
-
* forming no cadence are skipped.
|
|
981
|
+
* Each temporally adjacent segment pair is classified with
|
|
982
|
+
* {@link detectCadence}; pairs forming no cadence are skipped. Segments
|
|
983
|
+
* separated by a gap (a rest in the timeline) are not a chord-to-chord
|
|
984
|
+
* progression, so they are never paired.
|
|
626
985
|
*
|
|
627
986
|
* @param timeline The chord timeline to scan.
|
|
628
987
|
* @param key The prevailing key.
|
|
629
988
|
* @returns The cadences found, in time order.
|
|
989
|
+
* @example
|
|
990
|
+
* ```ts
|
|
991
|
+
* import { chordTimelineFromNotes, detectCadences } from '@libraz/libcantus';
|
|
992
|
+
* const notes = [
|
|
993
|
+
* { pitch: 67, startBeat: 0, durationBeat: 4 }, // G, a dominant
|
|
994
|
+
* { pitch: 60, startBeat: 4, durationBeat: 4 }, // C, the tonic
|
|
995
|
+
* ];
|
|
996
|
+
* const { timeline, key } = chordTimelineFromNotes(notes);
|
|
997
|
+
* const cadences = detectCadences(timeline, key);
|
|
998
|
+
* ```
|
|
999
|
+
* @category Arrangement & Analysis
|
|
630
1000
|
*/
|
|
631
1001
|
declare function detectCadences(timeline: ChordTimeline, key: KeyScale): CadenceHit[];
|
|
632
1002
|
|
|
@@ -640,22 +1010,38 @@ declare function detectCadences(timeline: ChordTimeline, key: KeyScale): Cadence
|
|
|
640
1010
|
* curve is sampled across the piece.
|
|
641
1011
|
*/
|
|
642
1012
|
|
|
643
|
-
/**
|
|
1013
|
+
/**
|
|
1014
|
+
* The musical role a track plays in the arrangement.
|
|
1015
|
+
*
|
|
1016
|
+
* @category Arrangement & Analysis
|
|
1017
|
+
*/
|
|
644
1018
|
type TrackRole = 'melody' | 'harmony' | 'bass' | 'other';
|
|
645
|
-
/**
|
|
1019
|
+
/**
|
|
1020
|
+
* One input track: its notes plus optional name and role.
|
|
1021
|
+
*
|
|
1022
|
+
* @category Arrangement & Analysis
|
|
1023
|
+
*/
|
|
646
1024
|
type ArrangementTrack = {
|
|
647
1025
|
name?: string;
|
|
648
1026
|
role?: TrackRole;
|
|
649
1027
|
notes: NoteEvent[];
|
|
650
1028
|
};
|
|
651
|
-
/**
|
|
1029
|
+
/**
|
|
1030
|
+
* A track after analysis: its resolved name, role, and per-note annotations.
|
|
1031
|
+
*
|
|
1032
|
+
* @category Arrangement & Analysis
|
|
1033
|
+
*/
|
|
652
1034
|
type TrackAnalysis = {
|
|
653
1035
|
name: string;
|
|
654
1036
|
role: TrackRole;
|
|
655
1037
|
/** One {@link AnalyzedNote} per note, labelled against the inferred chords. */
|
|
656
1038
|
notes: AnalyzedNote[];
|
|
657
1039
|
};
|
|
658
|
-
/**
|
|
1040
|
+
/**
|
|
1041
|
+
* A note that clashes with the harmony sounding beneath it.
|
|
1042
|
+
*
|
|
1043
|
+
* @category Arrangement & Analysis
|
|
1044
|
+
*/
|
|
659
1045
|
type Conflict = {
|
|
660
1046
|
beat: number;
|
|
661
1047
|
trackName: string;
|
|
@@ -666,7 +1052,11 @@ type Conflict = {
|
|
|
666
1052
|
reasons: number;
|
|
667
1053
|
rationale?: string;
|
|
668
1054
|
};
|
|
669
|
-
/**
|
|
1055
|
+
/**
|
|
1056
|
+
* The full result of {@link analyzeArrangement}.
|
|
1057
|
+
*
|
|
1058
|
+
* @category Arrangement & Analysis
|
|
1059
|
+
*/
|
|
670
1060
|
type ArrangementAnalysis = {
|
|
671
1061
|
key: KeyScale;
|
|
672
1062
|
timeline: ChordTimeline;
|
|
@@ -676,15 +1066,31 @@ type ArrangementAnalysis = {
|
|
|
676
1066
|
/** Notes clashing with the sounding harmony, worst severity first. */
|
|
677
1067
|
conflicts: Conflict[];
|
|
678
1068
|
};
|
|
679
|
-
/**
|
|
1069
|
+
/**
|
|
1070
|
+
* Options controlling {@link analyzeArrangement} and {@link tensionCurve}.
|
|
1071
|
+
*
|
|
1072
|
+
* @category Arrangement & Analysis
|
|
1073
|
+
*/
|
|
680
1074
|
type ArrangementOptions = {
|
|
681
1075
|
/** Key context; inferred from the pooled notes when omitted. */
|
|
682
1076
|
key?: KeyScale;
|
|
683
|
-
/**
|
|
1077
|
+
/**
|
|
1078
|
+
* Time signature; defaults to 4/4.
|
|
1079
|
+
*
|
|
1080
|
+
* @defaultValue `4/4`
|
|
1081
|
+
*/
|
|
684
1082
|
ts?: TimeSignature;
|
|
685
|
-
/**
|
|
1083
|
+
/**
|
|
1084
|
+
* Chord-slot length in beats; defaults to one bar of `ts`.
|
|
1085
|
+
*
|
|
1086
|
+
* @defaultValue one bar of `ts`
|
|
1087
|
+
*/
|
|
686
1088
|
harmonicRhythm?: number;
|
|
687
|
-
/**
|
|
1089
|
+
/**
|
|
1090
|
+
* Safety profile used for conflict detection; defaults to `pop`.
|
|
1091
|
+
*
|
|
1092
|
+
* @defaultValue `'pop'`
|
|
1093
|
+
*/
|
|
688
1094
|
profile?: SafetyProfile;
|
|
689
1095
|
};
|
|
690
1096
|
/**
|
|
@@ -693,18 +1099,42 @@ type ArrangementOptions = {
|
|
|
693
1099
|
* The chord timeline and key are inferred from the pooled notes of every track
|
|
694
1100
|
* (see {@link chordTimelineFromNotes}); pooling all voices is robust even when
|
|
695
1101
|
* roles are absent or a track doubles the harmony, so it is preferred over
|
|
696
|
-
* deriving the harmony from a subset.
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
1102
|
+
* deriving the harmony from a subset. Zero- and negative-length notes never
|
|
1103
|
+
* sound, so they are dropped at ingest and appear in neither the annotations
|
|
1104
|
+
* nor the conflicts.
|
|
1105
|
+
*
|
|
1106
|
+
* Voice analysis assumes one monophonic voice at a time, so each track is
|
|
1107
|
+
* first partitioned into monophonic sub-voices (a polyphonic block-chord track
|
|
1108
|
+
* fans out into one sub-voice per simultaneous note; a monophonic track is
|
|
1109
|
+
* unaffected). Each sub-voice is labelled note-by-note against the harmony
|
|
1110
|
+
* with {@link analyzeVoice}, seeing every other sub-voice — including siblings
|
|
1111
|
+
* within the same track — as its accompaniment. Every note is then re-checked
|
|
1112
|
+
* with {@link evaluateSafety} at its onset and again at each chord change it
|
|
1113
|
+
* sustains across, so a held note that clashes with a later chord is caught:
|
|
1114
|
+
* evaluations that are not {@link NoteSafety.Safe} become conflicts, one per
|
|
1115
|
+
* clashing beat, sorted worst severity first and then by beat.
|
|
701
1116
|
*
|
|
702
1117
|
* @param tracks The tracks to analyse.
|
|
703
1118
|
* @param opts Analysis options; see {@link ArrangementOptions}.
|
|
704
1119
|
* @returns The inferred harmony, per-track annotations, cadences, and conflicts.
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```ts
|
|
1122
|
+
* import { analyzeArrangement } from '@libraz/libcantus';
|
|
1123
|
+
* const melody = [
|
|
1124
|
+
* { pitch: 60, startBeat: 0, durationBeat: 2 },
|
|
1125
|
+
* { pitch: 67, startBeat: 2, durationBeat: 2 },
|
|
1126
|
+
* ];
|
|
1127
|
+
* const { key, conflicts } = analyzeArrangement([{ role: 'melody', notes: melody }]);
|
|
1128
|
+
* conflicts; // notes clashing with the inferred harmony, worst severity first
|
|
1129
|
+
* ```
|
|
1130
|
+
* @category Arrangement & Analysis
|
|
705
1131
|
*/
|
|
706
1132
|
declare function analyzeArrangement(tracks: ArrangementTrack[], opts?: ArrangementOptions): ArrangementAnalysis;
|
|
707
|
-
/**
|
|
1133
|
+
/**
|
|
1134
|
+
* A tension reading sampled at a beat.
|
|
1135
|
+
*
|
|
1136
|
+
* @category Arrangement & Analysis
|
|
1137
|
+
*/
|
|
708
1138
|
type TensionPoint = {
|
|
709
1139
|
beat: number;
|
|
710
1140
|
/** Combined tension in [0, 1]. */
|
|
@@ -729,6 +1159,14 @@ type TensionPoint = {
|
|
|
729
1159
|
* @param opts Analysis options plus an optional `step` (default one beat).
|
|
730
1160
|
* @returns One {@link TensionPoint} per sampled beat, in beat order.
|
|
731
1161
|
* @throws If `step` is not positive.
|
|
1162
|
+
* @example
|
|
1163
|
+
* ```ts
|
|
1164
|
+
* import { tensionCurve } from '@libraz/libcantus';
|
|
1165
|
+
* const notes = [{ pitch: 60, startBeat: 0, durationBeat: 4 }];
|
|
1166
|
+
* const curve = tensionCurve([{ notes }], { step: 1 });
|
|
1167
|
+
* curve; // one { beat, tension } sample per beat, in beat order
|
|
1168
|
+
* ```
|
|
1169
|
+
* @category Arrangement & Analysis
|
|
732
1170
|
*/
|
|
733
1171
|
declare function tensionCurve(tracks: ArrangementTrack[], opts?: ArrangementOptions & {
|
|
734
1172
|
step?: number;
|
|
@@ -748,27 +1186,55 @@ declare function tensionCurve(tracks: ArrangementTrack[], opts?: ArrangementOpti
|
|
|
748
1186
|
* library-wide convention.
|
|
749
1187
|
*/
|
|
750
1188
|
|
|
751
|
-
/**
|
|
1189
|
+
/**
|
|
1190
|
+
* A chord sounding over a half-open beat span `[startBeat, endBeat)`.
|
|
1191
|
+
*
|
|
1192
|
+
* @category Composition
|
|
1193
|
+
*/
|
|
752
1194
|
type BassSegment = {
|
|
753
1195
|
startBeat: number;
|
|
754
1196
|
endBeat: number;
|
|
755
1197
|
chord: Chord$1;
|
|
756
1198
|
};
|
|
757
|
-
/**
|
|
1199
|
+
/**
|
|
1200
|
+
* The bass-line idiom to generate.
|
|
1201
|
+
*
|
|
1202
|
+
* @category Composition
|
|
1203
|
+
*/
|
|
758
1204
|
type BassStyle = 'root' | 'rootFifth' | 'pop' | 'walking' | 'arpeggio';
|
|
759
|
-
/**
|
|
1205
|
+
/**
|
|
1206
|
+
* Options controlling {@link generateBassLine}.
|
|
1207
|
+
*
|
|
1208
|
+
* @category Composition
|
|
1209
|
+
*/
|
|
760
1210
|
type BassLineOptions = {
|
|
761
1211
|
/** Chord placement to follow; need not be pre-sorted. */
|
|
762
1212
|
segments: BassSegment[];
|
|
763
1213
|
/** Key/scale context, used for diatonic approach tones in `walking`. */
|
|
764
1214
|
key: KeyScale;
|
|
765
|
-
/**
|
|
1215
|
+
/**
|
|
1216
|
+
* Time signature; used for metric accents.
|
|
1217
|
+
*
|
|
1218
|
+
* @defaultValue `{ numerator: 4, denominator: 4 }`
|
|
1219
|
+
*/
|
|
766
1220
|
ts?: TimeSignature;
|
|
767
|
-
/**
|
|
1221
|
+
/**
|
|
1222
|
+
* Bass-line idiom.
|
|
1223
|
+
*
|
|
1224
|
+
* @defaultValue `'root'`
|
|
1225
|
+
*/
|
|
768
1226
|
style?: BassStyle;
|
|
769
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* Target register as a base MIDI octave; roots land around `octave*12+12`.
|
|
1229
|
+
*
|
|
1230
|
+
* @defaultValue 2
|
|
1231
|
+
*/
|
|
770
1232
|
octave?: number;
|
|
771
|
-
/**
|
|
1233
|
+
/**
|
|
1234
|
+
* Seed for the deterministic PRNG.
|
|
1235
|
+
*
|
|
1236
|
+
* @defaultValue 0
|
|
1237
|
+
*/
|
|
772
1238
|
seed?: number;
|
|
773
1239
|
};
|
|
774
1240
|
/**
|
|
@@ -781,6 +1247,18 @@ type BassLineOptions = {
|
|
|
781
1247
|
*
|
|
782
1248
|
* @param opts Segments, key, and generation options.
|
|
783
1249
|
* @returns Bass notes sorted by onset, non-overlapping.
|
|
1250
|
+
*
|
|
1251
|
+
* @example
|
|
1252
|
+
* ```ts
|
|
1253
|
+
* import { generateBassLine, makeChord, majorKey } from '@libraz/libcantus';
|
|
1254
|
+
* const segments = [
|
|
1255
|
+
* { startBeat: 0, endBeat: 4, chord: makeChord(0, 'maj') },
|
|
1256
|
+
* { startBeat: 4, endBeat: 8, chord: makeChord(7, 'maj') },
|
|
1257
|
+
* ];
|
|
1258
|
+
* const notes = generateBassLine({ segments, key: majorKey(0), style: 'walking' });
|
|
1259
|
+
* ```
|
|
1260
|
+
*
|
|
1261
|
+
* @category Composition
|
|
784
1262
|
*/
|
|
785
1263
|
declare function generateBassLine(opts: BassLineOptions): NoteEvent[];
|
|
786
1264
|
|
|
@@ -791,9 +1269,15 @@ declare function generateBassLine(opts: BassLineOptions): NoteEvent[];
|
|
|
791
1269
|
* @param scaleMask The scale's 12-bit mode mask.
|
|
792
1270
|
* @param scaleRootPc The pitch class the mask is rooted on.
|
|
793
1271
|
* @returns True if the scale is a superset of the chord.
|
|
1272
|
+
*
|
|
1273
|
+
* @category Scales
|
|
794
1274
|
*/
|
|
795
1275
|
declare function scaleMatchesChord(chordPcs: number[], scaleMask: number, scaleRootPc: number): boolean;
|
|
796
|
-
/**
|
|
1276
|
+
/**
|
|
1277
|
+
* A named scale rooted on a pitch class that fits over a chord.
|
|
1278
|
+
*
|
|
1279
|
+
* @category Scales
|
|
1280
|
+
*/
|
|
797
1281
|
type ChordScaleMatch = {
|
|
798
1282
|
name: string;
|
|
799
1283
|
rootPc: number;
|
|
@@ -803,13 +1287,26 @@ type ChordScaleMatch = {
|
|
|
803
1287
|
*
|
|
804
1288
|
* Only the chord root is considered as the scale root, matching the
|
|
805
1289
|
* conventional chord-scale relationship. Every entry of {@link NAMED_SCALES}
|
|
806
|
-
* whose pitch-class set is a superset of the chord's is returned
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
1290
|
+
* whose pitch-class set is a superset of the chord's is returned once per
|
|
1291
|
+
* distinct pitch-class set (aliased masks such as major/ionian report only the
|
|
1292
|
+
* modal name), ranked by fewest extra scale tones beyond the chord, then by
|
|
1293
|
+
* scale size (heptatonic before larger scales), then by scale name. For bare
|
|
1294
|
+
* triads and smaller chords, heptatonic scales rank before pentatonics and
|
|
1295
|
+
* other sizes: a pentatonic adds no modal color over a triad, so the seven-note
|
|
1296
|
+
* modes are the more useful answer. The chromatic scale is only returned as a
|
|
1297
|
+
* fallback when no other scale contains the chord.
|
|
810
1298
|
*
|
|
811
1299
|
* @param chord The chord to fit scales over.
|
|
812
1300
|
* @returns The matching scales rooted on the chord root, best fit first.
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```ts
|
|
1304
|
+
* import { makeChord, chordScales } from '@libraz/libcantus';
|
|
1305
|
+
* const scales = chordScales(makeChord(0, 'maj7'));
|
|
1306
|
+
* // scales for Cmaj7, best fit first; each is { name, rootPc: 0 }
|
|
1307
|
+
* ```
|
|
1308
|
+
*
|
|
1309
|
+
* @category Scales
|
|
813
1310
|
*/
|
|
814
1311
|
declare function chordScales(chord: Chord$1): ChordScaleMatch[];
|
|
815
1312
|
/**
|
|
@@ -822,6 +1319,8 @@ declare function chordScales(chord: Chord$1): ChordScaleMatch[];
|
|
|
822
1319
|
* @param chord The chord providing the chord tones.
|
|
823
1320
|
* @param scaleName A key of {@link NAMED_SCALES}.
|
|
824
1321
|
* @returns The avoid-note pitch classes, sorted ascending in [0, 11].
|
|
1322
|
+
*
|
|
1323
|
+
* @category Scales
|
|
825
1324
|
*/
|
|
826
1325
|
declare function avoidNotes(chord: Chord$1, scaleName: string): number[];
|
|
827
1326
|
/**
|
|
@@ -835,9 +1334,21 @@ declare function avoidNotes(chord: Chord$1, scaleName: string): number[];
|
|
|
835
1334
|
* @param chord The chord providing the chord tones.
|
|
836
1335
|
* @param scaleName A key of {@link NAMED_SCALES}.
|
|
837
1336
|
* @returns The available-tension pitch classes, sorted ascending in [0, 11].
|
|
1337
|
+
*
|
|
1338
|
+
* @example
|
|
1339
|
+
* ```ts
|
|
1340
|
+
* import { makeChord, availableTensions } from '@libraz/libcantus';
|
|
1341
|
+
* availableTensions(makeChord(0, 'maj7'), 'ionian'); // [2, 9] — color tones over Cmaj7
|
|
1342
|
+
* ```
|
|
1343
|
+
*
|
|
1344
|
+
* @category Scales
|
|
838
1345
|
*/
|
|
839
1346
|
declare function availableTensions(chord: Chord$1, scaleName: string): number[];
|
|
840
|
-
/**
|
|
1347
|
+
/**
|
|
1348
|
+
* A scale fit over a chord together with its avoid notes and tensions.
|
|
1349
|
+
*
|
|
1350
|
+
* @category Scales
|
|
1351
|
+
*/
|
|
841
1352
|
type ChordScaleReportEntry = {
|
|
842
1353
|
name: string;
|
|
843
1354
|
rootPc: number;
|
|
@@ -853,9 +1364,15 @@ type ChordScaleReportEntry = {
|
|
|
853
1364
|
* @param chord The chord to analyze.
|
|
854
1365
|
* @param limit Optional maximum number of scales to report; all by default.
|
|
855
1366
|
* @returns One entry per reported scale, best fit first.
|
|
1367
|
+
*
|
|
1368
|
+
* @category Scales
|
|
856
1369
|
*/
|
|
857
1370
|
declare function chordScaleReport(chord: Chord$1, limit?: number): ChordScaleReportEntry[];
|
|
858
|
-
/**
|
|
1371
|
+
/**
|
|
1372
|
+
* A chord paired with the scale chosen for it by {@link scalesForChanges}.
|
|
1373
|
+
*
|
|
1374
|
+
* @category Scales
|
|
1375
|
+
*/
|
|
859
1376
|
type ScaleChoice = {
|
|
860
1377
|
chord: Chord$1;
|
|
861
1378
|
scale: ChordScaleMatch;
|
|
@@ -872,10 +1389,16 @@ type ScaleChoice = {
|
|
|
872
1389
|
*
|
|
873
1390
|
* @param chords The chord sequence to choose scales for.
|
|
874
1391
|
* @returns One scale choice per chord, in input order.
|
|
1392
|
+
*
|
|
1393
|
+
* @category Scales
|
|
875
1394
|
*/
|
|
876
1395
|
declare function scalesForChanges(chords: Chord$1[]): ScaleChoice[];
|
|
877
1396
|
|
|
878
|
-
/**
|
|
1397
|
+
/**
|
|
1398
|
+
* Options controlling {@link generateCounterMelody}.
|
|
1399
|
+
*
|
|
1400
|
+
* @category Voicing & Counterpoint
|
|
1401
|
+
*/
|
|
879
1402
|
type CounterMelodyOptions = {
|
|
880
1403
|
/** The lead line to write against, in ascending onset order. */
|
|
881
1404
|
melody: NoteEvent[];
|
|
@@ -883,22 +1406,40 @@ type CounterMelodyOptions = {
|
|
|
883
1406
|
chordAt: (beat: number) => Chord$1 | null;
|
|
884
1407
|
/** Key/scale context for scale-tone decisions. */
|
|
885
1408
|
key: KeyScale;
|
|
886
|
-
/**
|
|
1409
|
+
/**
|
|
1410
|
+
* Meter used for strong-beat decisions.
|
|
1411
|
+
*
|
|
1412
|
+
* @defaultValue 4/4
|
|
1413
|
+
*/
|
|
887
1414
|
ts?: TimeSignature;
|
|
888
|
-
/**
|
|
1415
|
+
/**
|
|
1416
|
+
* Which side of the melody the counter line occupies.
|
|
1417
|
+
*
|
|
1418
|
+
* @defaultValue 'below'
|
|
1419
|
+
*/
|
|
889
1420
|
register?: 'above' | 'below';
|
|
890
1421
|
/**
|
|
891
|
-
* Onset strategy: `'complement'`
|
|
892
|
-
*
|
|
1422
|
+
* Onset strategy: `'complement'` moves where the melody holds or rests and
|
|
1423
|
+
* reinforces some strong beats; `'follow'` mirrors melody onsets.
|
|
1424
|
+
*
|
|
1425
|
+
* @defaultValue 'complement'
|
|
893
1426
|
*/
|
|
894
1427
|
rhythm?: 'complement' | 'follow';
|
|
895
|
-
/**
|
|
1428
|
+
/**
|
|
1429
|
+
* Safety profile applied to candidate pitches.
|
|
1430
|
+
*
|
|
1431
|
+
* @defaultValue 'pop'
|
|
1432
|
+
*/
|
|
896
1433
|
profile?: 'strict' | 'pop';
|
|
897
1434
|
/** Lowest MIDI pitch the counter line may use (default derived from `register`). */
|
|
898
1435
|
pitchLow?: number;
|
|
899
1436
|
/** Highest MIDI pitch the counter line may use (default derived from `register`). */
|
|
900
1437
|
pitchHigh?: number;
|
|
901
|
-
/**
|
|
1438
|
+
/**
|
|
1439
|
+
* PRNG seed; the same seed always yields the same line.
|
|
1440
|
+
*
|
|
1441
|
+
* @defaultValue 0
|
|
1442
|
+
*/
|
|
902
1443
|
seed?: number;
|
|
903
1444
|
};
|
|
904
1445
|
/**
|
|
@@ -917,6 +1458,17 @@ type CounterMelodyOptions = {
|
|
|
917
1458
|
*
|
|
918
1459
|
* @param opts The lead line, chord context callback, key, and generation knobs.
|
|
919
1460
|
* @returns The counter line as note events sorted by onset; `[]` for an empty melody.
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```ts
|
|
1463
|
+
* import { generateCounterMelody, majorKey, parseChordSymbol } from '@libraz/libcantus';
|
|
1464
|
+
* const melody = [{ pitch: 72, startBeat: 0, durationBeat: 2 }];
|
|
1465
|
+
* const counter = generateCounterMelody({
|
|
1466
|
+
* melody,
|
|
1467
|
+
* chordAt: () => parseChordSymbol('C'),
|
|
1468
|
+
* key: majorKey(0),
|
|
1469
|
+
* }); // note events below the melody, in onset order
|
|
1470
|
+
* ```
|
|
1471
|
+
* @category Voicing & Counterpoint
|
|
920
1472
|
*/
|
|
921
1473
|
declare function generateCounterMelody(opts: CounterMelodyOptions): NoteEvent[];
|
|
922
1474
|
|
|
@@ -926,6 +1478,7 @@ declare function generateCounterMelody(opts: CounterMelodyOptions): NoteEvent[];
|
|
|
926
1478
|
* @param upperPitch Pitch of the nominally higher voice.
|
|
927
1479
|
* @param lowerPitch Pitch of the nominally lower voice.
|
|
928
1480
|
* @returns True when the upper voice sits below the lower voice.
|
|
1481
|
+
* @category Voicing & Counterpoint
|
|
929
1482
|
*/
|
|
930
1483
|
declare function createsVoiceCrossing(upperPitch: number, lowerPitch: number): boolean;
|
|
931
1484
|
/**
|
|
@@ -935,6 +1488,7 @@ declare function createsVoiceCrossing(upperPitch: number, lowerPitch: number): b
|
|
|
935
1488
|
* @param b Second pitch.
|
|
936
1489
|
* @param twoVoice When true, the perfect fourth counts as dissonant.
|
|
937
1490
|
* @returns True when the vertical interval is dissonant.
|
|
1491
|
+
* @category Voicing & Counterpoint
|
|
938
1492
|
*/
|
|
939
1493
|
declare function createsVerticalDissonance(a: number, b: number, twoVoice: boolean): boolean;
|
|
940
1494
|
/**
|
|
@@ -947,6 +1501,7 @@ declare function createsVerticalDissonance(a: number, b: number, twoVoice: boole
|
|
|
947
1501
|
* @param prev Starting pitch.
|
|
948
1502
|
* @param cur Ending pitch.
|
|
949
1503
|
* @returns True when the leap is forbidden in strict counterpoint.
|
|
1504
|
+
* @category Voicing & Counterpoint
|
|
950
1505
|
*/
|
|
951
1506
|
declare function isForbiddenMelodicLeap(prev: number, cur: number): boolean;
|
|
952
1507
|
/**
|
|
@@ -962,16 +1517,29 @@ declare function isForbiddenMelodicLeap(prev: number, cur: number): boolean;
|
|
|
962
1517
|
* A fifth expanding to a twelfth counts (same perfect class); a fifth moving to
|
|
963
1518
|
* an octave does not (different perfect kinds — the direct/hidden case owned by
|
|
964
1519
|
* {@link createsHiddenParallelPerfect}).
|
|
1520
|
+
*
|
|
1521
|
+
* @category Voicing & Counterpoint
|
|
965
1522
|
*/
|
|
966
1523
|
declare function createsParallelPerfect(aPrev: number, aCur: number, bPrev: number, bCur: number): boolean;
|
|
967
1524
|
/**
|
|
968
1525
|
* Whether two voices move in consecutive parallel octaves (or unisons) by
|
|
969
1526
|
* similar motion.
|
|
1527
|
+
*
|
|
1528
|
+
* This is a strict subset of {@link createsParallelPerfect}: a similar-motion
|
|
1529
|
+
* octave-to-octave is the perfect-class-zero case that predicate already flags
|
|
1530
|
+
* (and it additionally catches the contrary-motion anti-parallel). Callers that
|
|
1531
|
+
* tally parallel violations should therefore use {@link createsParallelPerfect}
|
|
1532
|
+
* alone to avoid double counting; this predicate remains for callers wanting a
|
|
1533
|
+
* dedicated similar-motion octave test.
|
|
1534
|
+
*
|
|
1535
|
+
* @category Voicing & Counterpoint
|
|
970
1536
|
*/
|
|
971
1537
|
declare function createsParallelOctave(aPrev: number, aCur: number, bPrev: number, bCur: number): boolean;
|
|
972
1538
|
/**
|
|
973
1539
|
* Whether two voices move in consecutive parallel unisons — both landing on the
|
|
974
1540
|
* same pitch, having shared a pitch on the previous move.
|
|
1541
|
+
*
|
|
1542
|
+
* @category Voicing & Counterpoint
|
|
975
1543
|
*/
|
|
976
1544
|
declare function createsParallelUnison(aPrev: number, aCur: number, bPrev: number, bCur: number): boolean;
|
|
977
1545
|
/**
|
|
@@ -981,6 +1549,8 @@ declare function createsParallelUnison(aPrev: number, aCur: number, bPrev: numbe
|
|
|
981
1549
|
* The traditional step exception is applied: the approach is allowed when the
|
|
982
1550
|
* upper of the two voices moves by step, so only leaps into the perfect
|
|
983
1551
|
* interval are flagged.
|
|
1552
|
+
*
|
|
1553
|
+
* @category Voicing & Counterpoint
|
|
984
1554
|
*/
|
|
985
1555
|
declare function createsHiddenParallelPerfect(aPrev: number, aCur: number, bPrev: number, bCur: number): boolean;
|
|
986
1556
|
/**
|
|
@@ -992,6 +1562,7 @@ declare function createsHiddenParallelPerfect(aPrev: number, aCur: number, bPrev
|
|
|
992
1562
|
* @param upperCur Current pitch of the upper voice.
|
|
993
1563
|
* @param lowerPrev Previous pitch of the lower voice.
|
|
994
1564
|
* @param lowerCur Current pitch of the lower voice.
|
|
1565
|
+
* @category Voicing & Counterpoint
|
|
995
1566
|
*/
|
|
996
1567
|
declare function createsVoiceOverlap(upperPrev: number, upperCur: number, lowerPrev: number, lowerCur: number): boolean;
|
|
997
1568
|
/**
|
|
@@ -1002,6 +1573,7 @@ declare function createsVoiceOverlap(upperPrev: number, upperCur: number, lowerP
|
|
|
1002
1573
|
* @param upperPitch Pitch of the higher voice.
|
|
1003
1574
|
* @param lowerPitch Pitch of the lower voice.
|
|
1004
1575
|
* @param maxSemitones Maximum allowed spacing in semitones (default an octave).
|
|
1576
|
+
* @category Voicing & Counterpoint
|
|
1005
1577
|
*/
|
|
1006
1578
|
declare function exceedsSpacing(upperPitch: number, lowerPitch: number, maxSemitones?: number): boolean;
|
|
1007
1579
|
/**
|
|
@@ -1011,6 +1583,7 @@ declare function exceedsSpacing(upperPitch: number, lowerPitch: number, maxSemit
|
|
|
1011
1583
|
* @param cur The following pitch.
|
|
1012
1584
|
* @param key Key context supplying the tonic.
|
|
1013
1585
|
* @returns True when `prev` is the leading tone and `cur` is the tonic a step above.
|
|
1586
|
+
* @category Voicing & Counterpoint
|
|
1014
1587
|
*/
|
|
1015
1588
|
declare function isLeadingToneResolution(prev: number, cur: number, key: KeyScale): boolean;
|
|
1016
1589
|
|
|
@@ -1020,7 +1593,11 @@ declare function isLeadingToneResolution(prev: number, cur: number, key: KeyScal
|
|
|
1020
1593
|
* which only go name -> notes.
|
|
1021
1594
|
*/
|
|
1022
1595
|
|
|
1023
|
-
/**
|
|
1596
|
+
/**
|
|
1597
|
+
* A candidate chord interpretation of a pitch set.
|
|
1598
|
+
*
|
|
1599
|
+
* @category Recognition
|
|
1600
|
+
*/
|
|
1024
1601
|
type ChordMatch = {
|
|
1025
1602
|
rootPc: number;
|
|
1026
1603
|
quality: ChordQuality;
|
|
@@ -1035,7 +1612,11 @@ type ChordMatch = {
|
|
|
1035
1612
|
/** Bass pitch class when inverted (the lowest note is not the root). */
|
|
1036
1613
|
bassPc?: number;
|
|
1037
1614
|
};
|
|
1038
|
-
/**
|
|
1615
|
+
/**
|
|
1616
|
+
* A candidate key interpretation of a pitch-class set.
|
|
1617
|
+
*
|
|
1618
|
+
* @category Recognition
|
|
1619
|
+
*/
|
|
1039
1620
|
type KeyMatch = {
|
|
1040
1621
|
key: KeyScale;
|
|
1041
1622
|
mode: 'major' | 'minor';
|
|
@@ -1054,6 +1635,13 @@ type KeyMatch = {
|
|
|
1054
1635
|
*
|
|
1055
1636
|
* @param pitches MIDI pitches or bare pitch classes (octave-agnostic).
|
|
1056
1637
|
* @returns Ranked chord interpretations (may be empty).
|
|
1638
|
+
* @example
|
|
1639
|
+
* ```ts
|
|
1640
|
+
* import { detectChord } from '@libraz/libcantus';
|
|
1641
|
+
* const matches = detectChord([60, 64, 67]); // C E G
|
|
1642
|
+
* matches[0]; // { rootPc: 0, quality: 'maj', exact: true, ... }
|
|
1643
|
+
* ```
|
|
1644
|
+
* @category Recognition
|
|
1057
1645
|
*/
|
|
1058
1646
|
declare function detectChord(pitches: number[]): ChordMatch[];
|
|
1059
1647
|
/**
|
|
@@ -1061,16 +1649,34 @@ declare function detectChord(pitches: number[]): ChordMatch[];
|
|
|
1061
1649
|
*
|
|
1062
1650
|
* @param pitches MIDI pitches or bare pitch classes.
|
|
1063
1651
|
* @returns The top-ranked chord, or null when nothing matches.
|
|
1652
|
+
* @example
|
|
1653
|
+
* ```ts
|
|
1654
|
+
* import { detectChordBest } from '@libraz/libcantus';
|
|
1655
|
+
* detectChordBest([60, 64, 67]); // C major triad: { rootPc: 0, quality: 'maj', ... }
|
|
1656
|
+
* ```
|
|
1657
|
+
* @category Recognition
|
|
1064
1658
|
*/
|
|
1065
1659
|
declare function detectChordBest(pitches: number[]): Chord$1 | null;
|
|
1066
1660
|
/**
|
|
1067
1661
|
* Rank major and minor keys by how well they contain a set of pitch classes.
|
|
1068
1662
|
*
|
|
1069
1663
|
* The tonic is weighted so that, among equally-fitting keys, the one whose root
|
|
1070
|
-
* appears in the input is preferred.
|
|
1664
|
+
* appears in the input is preferred. Minor candidates are scored against the
|
|
1665
|
+
* natural, harmonic, and melodic minor variants and keep the best of the three,
|
|
1666
|
+
* so a minor cadence containing the leading tone still resolves to its own
|
|
1667
|
+
* tonic; the returned key is always the natural-minor scale. Returns all 24
|
|
1668
|
+
* keys ranked best-first, or an empty array for an empty input (mirroring
|
|
1669
|
+
* {@link detectChord}).
|
|
1071
1670
|
*
|
|
1072
1671
|
* @param pitches MIDI pitches or bare pitch classes.
|
|
1073
|
-
* @returns Ranked key interpretations.
|
|
1672
|
+
* @returns Ranked key interpretations (empty for an empty input).
|
|
1673
|
+
* @example
|
|
1674
|
+
* ```ts
|
|
1675
|
+
* import { detectKey } from '@libraz/libcantus';
|
|
1676
|
+
* const keys = detectKey([60, 62, 64, 65, 67, 69, 71]); // C major scale
|
|
1677
|
+
* keys[0].mode; // 'major', with keys[0].key.rootPc === 0
|
|
1678
|
+
* ```
|
|
1679
|
+
* @category Recognition
|
|
1074
1680
|
*/
|
|
1075
1681
|
declare function detectKey(pitches: number[]): KeyMatch[];
|
|
1076
1682
|
|
|
@@ -1085,27 +1691,87 @@ type DrumHit = {
|
|
|
1085
1691
|
/** Groove feel controlling off-beat swing. */
|
|
1086
1692
|
type Feel = 'straight' | 'swing' | 'shuffle';
|
|
1087
1693
|
/** Public groove style identifiers. */
|
|
1088
|
-
type GrooveStyle = 'standard' | 'funk' | 'shuffle' | 'bossa' | 'trap' | 'halftime' | 'breakbeat';
|
|
1694
|
+
type GrooveStyle = 'standard' | 'funk' | 'shuffle' | 'bossa' | 'trap' | 'halftime' | 'breakbeat' | 'house' | 'synthpop';
|
|
1089
1695
|
/** Public role identifiers gating which voices are present. */
|
|
1090
1696
|
type DrumRole = 'full' | 'ambient' | 'minimal' | 'fxOnly';
|
|
1091
1697
|
/** Public section identifiers. */
|
|
1092
1698
|
type PublicSection = 'intro' | 'verse' | 'prechorus' | 'chorus' | 'bridge' | 'outro';
|
|
1093
1699
|
|
|
1700
|
+
/**
|
|
1701
|
+
* Groove feel (the swing/straight rhythmic character) for {@link generateDrums}.
|
|
1702
|
+
*
|
|
1703
|
+
* @category Composition
|
|
1704
|
+
*/
|
|
1094
1705
|
type GrooveFeel = Feel;
|
|
1095
1706
|
|
|
1096
|
-
/**
|
|
1707
|
+
/**
|
|
1708
|
+
* Public section identifiers for {@link generateDrums}.
|
|
1709
|
+
*
|
|
1710
|
+
* @category Composition
|
|
1711
|
+
*/
|
|
1097
1712
|
type Section = PublicSection;
|
|
1098
|
-
/**
|
|
1713
|
+
/**
|
|
1714
|
+
* A Euclidean (Bjorklund) kick pattern for {@link generateDrums}.
|
|
1715
|
+
*
|
|
1716
|
+
* @category Composition
|
|
1717
|
+
*/
|
|
1718
|
+
type EuclideanKick = {
|
|
1719
|
+
/** Number of kick onsets, clamped to `[0, steps]`. */
|
|
1720
|
+
pulses: number;
|
|
1721
|
+
/**
|
|
1722
|
+
* Total steps in the bar (1..16).
|
|
1723
|
+
*
|
|
1724
|
+
* @defaultValue 16
|
|
1725
|
+
*/
|
|
1726
|
+
steps?: number;
|
|
1727
|
+
/**
|
|
1728
|
+
* Steps to rotate the onsets toward later positions.
|
|
1729
|
+
*
|
|
1730
|
+
* @defaultValue 0
|
|
1731
|
+
*/
|
|
1732
|
+
rotation?: number;
|
|
1733
|
+
};
|
|
1734
|
+
/**
|
|
1735
|
+
* Options controlling {@link generateDrums}.
|
|
1736
|
+
*
|
|
1737
|
+
* @category Composition
|
|
1738
|
+
*/
|
|
1099
1739
|
type DrumGenOptions = {
|
|
1100
1740
|
bars: number;
|
|
1101
1741
|
bpm: number;
|
|
1102
1742
|
style: GrooveStyle;
|
|
1103
1743
|
section: Section;
|
|
1104
1744
|
density: number;
|
|
1745
|
+
/**
|
|
1746
|
+
* Replace the final bar with a fill.
|
|
1747
|
+
*
|
|
1748
|
+
* @defaultValue false
|
|
1749
|
+
*/
|
|
1105
1750
|
fills?: boolean;
|
|
1106
1751
|
feel?: GrooveFeel;
|
|
1752
|
+
/**
|
|
1753
|
+
* Voicing role; `'fxOnly'` suppresses the main kick/snare/ghost/fill voices.
|
|
1754
|
+
*
|
|
1755
|
+
* @defaultValue `'full'`
|
|
1756
|
+
*/
|
|
1107
1757
|
role?: DrumRole;
|
|
1758
|
+
/**
|
|
1759
|
+
* Seed for the deterministic PRNG.
|
|
1760
|
+
*
|
|
1761
|
+
* @defaultValue 0
|
|
1762
|
+
*/
|
|
1108
1763
|
seed?: number;
|
|
1764
|
+
/**
|
|
1765
|
+
* Section the final-bar fill leads into. Shapes which fill archetype is
|
|
1766
|
+
* chosen (into-chorus and out-of-intro fills differ from generic ones).
|
|
1767
|
+
* Defaults to `section`, i.e. a within-section fill.
|
|
1768
|
+
*/
|
|
1769
|
+
nextSection?: Section;
|
|
1770
|
+
/**
|
|
1771
|
+
* When set, the kick follows this Euclidean rhythm instead of the
|
|
1772
|
+
* style/section pattern, giving direct access to evenly spread onsets.
|
|
1773
|
+
*/
|
|
1774
|
+
euclideanKick?: EuclideanKick;
|
|
1109
1775
|
};
|
|
1110
1776
|
/**
|
|
1111
1777
|
* Generate a drum performance as a flat list of onsets.
|
|
@@ -1115,11 +1781,23 @@ type DrumGenOptions = {
|
|
|
1115
1781
|
* by its General MIDI pitch. Groove style selects an internal style and feel;
|
|
1116
1782
|
* `density` sets the backing-density level; `section` shapes kick, hi-hat, ghost,
|
|
1117
1783
|
* and percussion density. 16th-note hi-hats drop to 8ths at or above 150 BPM.
|
|
1118
|
-
* When `fills` is true the final bar is replaced with a fill
|
|
1784
|
+
* When `fills` is true the final bar is replaced with a fill whose archetype is
|
|
1785
|
+
* shaped by `nextSection`; a fill that would emit nothing on its beat falls back
|
|
1786
|
+
* to the normal groove so the phrase end is never silent. `euclideanKick`
|
|
1787
|
+
* overrides the kick with an evenly-spread Euclidean pattern. Output is fully
|
|
1119
1788
|
* determined by the options plus `seed`.
|
|
1120
1789
|
*
|
|
1121
1790
|
* @param opts Generation options.
|
|
1122
1791
|
* @returns Percussion onsets in bar order.
|
|
1792
|
+
*
|
|
1793
|
+
* @example
|
|
1794
|
+
* ```ts
|
|
1795
|
+
* import { generateDrums } from '@libraz/libcantus';
|
|
1796
|
+
* const hits = generateDrums({ bars: 4, bpm: 120, style: 'standard', section: 'chorus', density: 0.6, fills: true });
|
|
1797
|
+
* // Fully determined by the options plus seed (defaults to 0).
|
|
1798
|
+
* ```
|
|
1799
|
+
*
|
|
1800
|
+
* @category Composition
|
|
1123
1801
|
*/
|
|
1124
1802
|
declare function generateDrums(opts: DrumGenOptions): DrumHit[];
|
|
1125
1803
|
|
|
@@ -1133,19 +1811,47 @@ declare function generateDrums(opts: DrumGenOptions): DrumHit[];
|
|
|
1133
1811
|
* seeded PRNG from the `random` module so results are fully reproducible.
|
|
1134
1812
|
*/
|
|
1135
1813
|
|
|
1136
|
-
/**
|
|
1814
|
+
/**
|
|
1815
|
+
* Options controlling {@link humanize}.
|
|
1816
|
+
*
|
|
1817
|
+
* @category Rhythm & Meter
|
|
1818
|
+
*/
|
|
1137
1819
|
type HumanizeOptions = {
|
|
1138
|
-
/**
|
|
1820
|
+
/**
|
|
1821
|
+
* Time signature used to derive metric accents.
|
|
1822
|
+
*
|
|
1823
|
+
* @defaultValue 4/4
|
|
1824
|
+
*/
|
|
1139
1825
|
ts?: TimeSignature;
|
|
1140
|
-
/**
|
|
1826
|
+
/**
|
|
1827
|
+
* Maximum timing jitter in quarter-note beats, applied as ±this value.
|
|
1828
|
+
*
|
|
1829
|
+
* @defaultValue 0.02
|
|
1830
|
+
*/
|
|
1141
1831
|
timing?: number;
|
|
1142
|
-
/**
|
|
1832
|
+
/**
|
|
1833
|
+
* Maximum velocity jitter (MIDI units), applied as ±this value.
|
|
1834
|
+
*
|
|
1835
|
+
* @defaultValue 8
|
|
1836
|
+
*/
|
|
1143
1837
|
velocity?: number;
|
|
1144
|
-
/**
|
|
1838
|
+
/**
|
|
1839
|
+
* How much louder strong beats get relative to weak ones, in MIDI velocity units.
|
|
1840
|
+
*
|
|
1841
|
+
* @defaultValue 12
|
|
1842
|
+
*/
|
|
1145
1843
|
accent?: number;
|
|
1146
|
-
/**
|
|
1844
|
+
/**
|
|
1845
|
+
* Velocity assumed for events with no `velocity` of their own.
|
|
1846
|
+
*
|
|
1847
|
+
* @defaultValue 80
|
|
1848
|
+
*/
|
|
1147
1849
|
baseVelocity?: number;
|
|
1148
|
-
/**
|
|
1850
|
+
/**
|
|
1851
|
+
* Seed for the deterministic PRNG.
|
|
1852
|
+
*
|
|
1853
|
+
* @defaultValue 0
|
|
1854
|
+
*/
|
|
1149
1855
|
seed?: number;
|
|
1150
1856
|
};
|
|
1151
1857
|
/**
|
|
@@ -1166,16 +1872,35 @@ type HumanizeOptions = {
|
|
|
1166
1872
|
* @param events The events to humanize.
|
|
1167
1873
|
* @param opts Humanization options.
|
|
1168
1874
|
* @returns Humanized copies of `events`, in input order.
|
|
1875
|
+
* @example
|
|
1876
|
+
* ```ts
|
|
1877
|
+
* import { humanize } from '@libraz/libcantus';
|
|
1878
|
+
* const events = [{ pitch: 60, startBeat: 0, durationBeat: 1, velocity: 80 }];
|
|
1879
|
+
* humanize(events, { seed: 1, timing: 0.03 }); // copies with jittered timing and accented velocity
|
|
1880
|
+
* ```
|
|
1881
|
+
* @category Rhythm & Meter
|
|
1169
1882
|
*/
|
|
1170
1883
|
declare function humanize(events: NoteEvent[], opts?: HumanizeOptions): NoteEvent[];
|
|
1171
|
-
/**
|
|
1884
|
+
/**
|
|
1885
|
+
* The recorded feel at a single grid slot: an average timing deviation and velocity.
|
|
1886
|
+
*
|
|
1887
|
+
* @category Rhythm & Meter
|
|
1888
|
+
*/
|
|
1172
1889
|
type GrooveSlot = {
|
|
1173
1890
|
/** Average `actualStartBeat - quantizedBeat` (in quarter-note beats) for events landing on this slot. */
|
|
1174
1891
|
timingOffset: number;
|
|
1175
|
-
/**
|
|
1176
|
-
|
|
1892
|
+
/**
|
|
1893
|
+
* Average velocity of events landing on this slot, or `null` if no event
|
|
1894
|
+
* with a velocity did. `null` (not 0) is the "unrecorded" sentinel so that a
|
|
1895
|
+
* genuine velocity of 0 is preserved and applied like any other value.
|
|
1896
|
+
*/
|
|
1897
|
+
velocity: number | null;
|
|
1177
1898
|
};
|
|
1178
|
-
/**
|
|
1899
|
+
/**
|
|
1900
|
+
* A per-bar grid of timing and velocity deviations captured from a performance.
|
|
1901
|
+
*
|
|
1902
|
+
* @category Rhythm & Meter
|
|
1903
|
+
*/
|
|
1179
1904
|
type GrooveTemplate = {
|
|
1180
1905
|
/** Grid resolution: equal grid steps per quarter-note beat. */
|
|
1181
1906
|
subdivision: number;
|
|
@@ -1183,34 +1908,70 @@ type GrooveTemplate = {
|
|
|
1183
1908
|
slotsPerBar: number;
|
|
1184
1909
|
/** One entry per grid slot, in slot-index order (slot 0 is the downbeat). */
|
|
1185
1910
|
slots: GrooveSlot[];
|
|
1911
|
+
/**
|
|
1912
|
+
* The time signature the template was extracted under. {@link applyGrooveTemplate}
|
|
1913
|
+
* requires the apply-time meter to match this so the per-bar grid lines up
|
|
1914
|
+
* instead of silently drifting. Optional for backward compatibility with
|
|
1915
|
+
* templates constructed without it (in which case no check is performed).
|
|
1916
|
+
*/
|
|
1917
|
+
ts?: TimeSignature;
|
|
1186
1918
|
};
|
|
1187
1919
|
/**
|
|
1188
1920
|
* Extract a groove template from a set of note events: for each grid slot,
|
|
1189
1921
|
* the average timing deviation from the quantized grid and the average
|
|
1190
|
-
* velocity of events landing on it. Slots with no
|
|
1191
|
-
* `
|
|
1922
|
+
* velocity of events landing on it. Slots with no velocity-carrying event
|
|
1923
|
+
* record `velocity: null` (the "unrecorded" sentinel); empty slots default to
|
|
1924
|
+
* `{ timingOffset: 0, velocity: null }`. The time signature is stored on the
|
|
1925
|
+
* template so {@link applyGrooveTemplate} can verify the meter matches.
|
|
1192
1926
|
*
|
|
1193
1927
|
* @param events The (typically "groovy", human-played) events to analyze.
|
|
1194
1928
|
* @param ts The time signature, used to compute the bar length.
|
|
1195
1929
|
* @param subdivision Grid resolution: equal grid steps per quarter-note beat.
|
|
1196
1930
|
* @returns The extracted groove template.
|
|
1931
|
+
* @category Rhythm & Meter
|
|
1197
1932
|
*/
|
|
1198
1933
|
declare function extractGrooveTemplate(events: NoteEvent[], ts: TimeSignature, subdivision: number): GrooveTemplate;
|
|
1199
1934
|
/**
|
|
1200
1935
|
* Apply a groove template to a set of (typically quantized) note events: each
|
|
1201
1936
|
* event is snapped to its grid slot and then offset by that slot's recorded
|
|
1202
1937
|
* timing deviation, pushing stiff timing toward the template's feel.
|
|
1203
|
-
* Velocity is set to the slot's recorded average when it recorded
|
|
1204
|
-
*
|
|
1938
|
+
* Velocity is set to the slot's recorded average when it recorded one (a
|
|
1939
|
+
* non-`null` velocity, including a genuine 0); otherwise the event's own
|
|
1940
|
+
* velocity is left untouched.
|
|
1941
|
+
*
|
|
1942
|
+
* If the template carries a time signature (see {@link GrooveTemplate.ts}) it
|
|
1943
|
+
* must match `ts`, since a differing bar length would misalign the per-bar
|
|
1944
|
+
* grid and drift the feel; a mismatch throws rather than corrupting timing
|
|
1945
|
+
* silently.
|
|
1205
1946
|
*
|
|
1206
1947
|
* @param events The events to reshape.
|
|
1207
1948
|
* @param template The groove template, from {@link extractGrooveTemplate}.
|
|
1208
1949
|
* @param ts The time signature, used to compute the bar length.
|
|
1209
1950
|
* @returns Reshaped copies of `events`, in input order.
|
|
1951
|
+
* @throws If `template.ts` is set and does not match `ts`.
|
|
1952
|
+
* @example
|
|
1953
|
+
* ```ts
|
|
1954
|
+
* import { extractGrooveTemplate, applyGrooveTemplate, parseTimeSignature } from '@libraz/libcantus';
|
|
1955
|
+
* const ts = parseTimeSignature('4/4');
|
|
1956
|
+
* // Learn the feel from a loosely played performance...
|
|
1957
|
+
* const groovy = [
|
|
1958
|
+
* { pitch: 36, startBeat: 0.02, durationBeat: 1, velocity: 100 },
|
|
1959
|
+
* { pitch: 38, startBeat: 1.06, durationBeat: 1, velocity: 70 },
|
|
1960
|
+
* ];
|
|
1961
|
+
* const template = extractGrooveTemplate(groovy, ts, 4);
|
|
1962
|
+
* // ...then impose it on stiff, quantized events.
|
|
1963
|
+
* const quantized = [{ pitch: 36, startBeat: 0, durationBeat: 1, velocity: 80 }];
|
|
1964
|
+
* applyGrooveTemplate(quantized, template, ts); // events nudged toward the captured feel
|
|
1965
|
+
* ```
|
|
1966
|
+
* @category Rhythm & Meter
|
|
1210
1967
|
*/
|
|
1211
1968
|
declare function applyGrooveTemplate(events: NoteEvent[], template: GrooveTemplate, ts: TimeSignature): NoteEvent[];
|
|
1212
1969
|
|
|
1213
|
-
/**
|
|
1970
|
+
/**
|
|
1971
|
+
* Harmonic role a pitch plays within a chord.
|
|
1972
|
+
*
|
|
1973
|
+
* @category Functional Harmony
|
|
1974
|
+
*/
|
|
1214
1975
|
type HarmonyRole = 'root' | 'third' | 'fifth' | 'sixth' | 'seventh' | 'tension' | 'doubling';
|
|
1215
1976
|
/**
|
|
1216
1977
|
* How firmly a pitch is locked to the chord's identity.
|
|
@@ -1218,9 +1979,15 @@ type HarmonyRole = 'root' | 'third' | 'fifth' | 'sixth' | 'seventh' | 'tension'
|
|
|
1218
1979
|
* - `identity`: moving it produces a different chord (the root).
|
|
1219
1980
|
* - `quality`: moving it flips the chord quality (the third).
|
|
1220
1981
|
* - `voicing`: it can move freely without changing chord identity or quality.
|
|
1982
|
+
*
|
|
1983
|
+
* @category Functional Harmony
|
|
1221
1984
|
*/
|
|
1222
1985
|
type LockLevel = 'identity' | 'quality' | 'voicing';
|
|
1223
|
-
/**
|
|
1986
|
+
/**
|
|
1987
|
+
* A pitch's role, lock level, and owning chord.
|
|
1988
|
+
*
|
|
1989
|
+
* @category Functional Harmony
|
|
1990
|
+
*/
|
|
1224
1991
|
type VoicedRole = {
|
|
1225
1992
|
role: HarmonyRole;
|
|
1226
1993
|
lock: LockLevel;
|
|
@@ -1243,12 +2010,27 @@ type VoicedRole = {
|
|
|
1243
2010
|
* @param chord The chord providing the root reference.
|
|
1244
2011
|
* @param chordId Identifier stored on the result (defaults to 0).
|
|
1245
2012
|
* @returns The pitch's role, lock level, and owning chord id.
|
|
2013
|
+
* @example
|
|
2014
|
+
* ```ts
|
|
2015
|
+
* import { roleOf, makeChord } from '@libraz/libcantus';
|
|
2016
|
+
* roleOf(64, makeChord(0, 'maj')); // E over C major
|
|
2017
|
+
* // { role: 'third', lock: 'quality', belongsToChordId: 0 }
|
|
2018
|
+
* ```
|
|
2019
|
+
* @category Functional Harmony
|
|
1246
2020
|
*/
|
|
1247
2021
|
declare function roleOf(pitch: number, chord: Chord$1, chordId?: number): VoicedRole;
|
|
1248
2022
|
|
|
1249
|
-
/**
|
|
2023
|
+
/**
|
|
2024
|
+
* A melody note supplied to the harmonizer: a {@link NoteEvent}.
|
|
2025
|
+
*
|
|
2026
|
+
* @category Reharmonization
|
|
2027
|
+
*/
|
|
1250
2028
|
type MelodyNote = NoteEvent;
|
|
1251
|
-
/**
|
|
2029
|
+
/**
|
|
2030
|
+
* Options controlling {@link harmonizeMelody}.
|
|
2031
|
+
*
|
|
2032
|
+
* @category Reharmonization
|
|
2033
|
+
*/
|
|
1252
2034
|
type HarmonizeOptions = {
|
|
1253
2035
|
melody: MelodyNote[];
|
|
1254
2036
|
key: KeyScale | 'infer';
|
|
@@ -1258,9 +2040,18 @@ type HarmonizeOptions = {
|
|
|
1258
2040
|
transposeSearch: boolean;
|
|
1259
2041
|
octaveSearch: boolean;
|
|
1260
2042
|
};
|
|
2043
|
+
/**
|
|
2044
|
+
* Seed for the deterministic tie-break perturbation.
|
|
2045
|
+
*
|
|
2046
|
+
* @defaultValue 0
|
|
2047
|
+
*/
|
|
1261
2048
|
seed?: number;
|
|
1262
2049
|
};
|
|
1263
|
-
/**
|
|
2050
|
+
/**
|
|
2051
|
+
* The chosen transpose, key, chord path, and per-note roles.
|
|
2052
|
+
*
|
|
2053
|
+
* @category Reharmonization
|
|
2054
|
+
*/
|
|
1264
2055
|
type HarmonizeResult = {
|
|
1265
2056
|
transposeSemitones: number;
|
|
1266
2057
|
key: KeyScale;
|
|
@@ -1281,13 +2072,39 @@ type HarmonizeResult = {
|
|
|
1281
2072
|
* `(transpose, progression)` pair is returned; a small tessitura cost breaks
|
|
1282
2073
|
* ties toward placements that keep the melody in a comfortable register.
|
|
1283
2074
|
*
|
|
2075
|
+
* The `seed` drives a deterministic tie-break only: it perturbs candidates by a
|
|
2076
|
+
* magnitude far below any real cost difference (see {@link TIE_BREAK_JITTER}),
|
|
2077
|
+
* so it can decide between chords or paths of otherwise-equal cost but never
|
|
2078
|
+
* overrides melody fit or functional flow. For a well-determined melody the
|
|
2079
|
+
* result is identical across seeds; the same seed always yields the same result.
|
|
2080
|
+
*
|
|
1284
2081
|
* @param opts Melody, key (or `'infer'`), harmonic rhythm, reharmonization
|
|
1285
2082
|
* strength, height-search flags, and seed.
|
|
1286
2083
|
* @returns The chosen transpose, key, chord path, and per-note roles.
|
|
2084
|
+
* @example
|
|
2085
|
+
* ```ts
|
|
2086
|
+
* import { harmonizeMelody, majorKey } from '@libraz/libcantus';
|
|
2087
|
+
* const result = harmonizeMelody({
|
|
2088
|
+
* melody: [
|
|
2089
|
+
* { pitch: 60, startBeat: 0, durationBeat: 1 },
|
|
2090
|
+
* { pitch: 64, startBeat: 1, durationBeat: 1 },
|
|
2091
|
+
* ],
|
|
2092
|
+
* key: majorKey(0),
|
|
2093
|
+
* harmonicRhythm: 1,
|
|
2094
|
+
* reharmonize: 'diatonic',
|
|
2095
|
+
* placement: { transposeSearch: false, octaveSearch: false },
|
|
2096
|
+
* });
|
|
2097
|
+
* result.chords; // one GeneratedChord per harmonic-rhythm segment
|
|
2098
|
+
* ```
|
|
2099
|
+
* @category Reharmonization
|
|
1287
2100
|
*/
|
|
1288
2101
|
declare function harmonizeMelody(opts: HarmonizeOptions): HarmonizeResult;
|
|
1289
2102
|
|
|
1290
|
-
/**
|
|
2103
|
+
/**
|
|
2104
|
+
* Counterpoint classification of a harmonic interval.
|
|
2105
|
+
*
|
|
2106
|
+
* @category Pitch & Intervals
|
|
2107
|
+
*/
|
|
1291
2108
|
declare enum IntervalQuality {
|
|
1292
2109
|
PerfectConsonance = 0,
|
|
1293
2110
|
ImperfectConsonance = 1,
|
|
@@ -1304,6 +2121,14 @@ declare enum IntervalQuality {
|
|
|
1304
2121
|
* @param semitones Interval size in semitones (may be negative or compound).
|
|
1305
2122
|
* @param twoVoice When true, the perfect fourth is treated as dissonant.
|
|
1306
2123
|
* @returns The counterpoint quality of the interval.
|
|
2124
|
+
* @example
|
|
2125
|
+
* ```ts
|
|
2126
|
+
* import { classifyInterval, IntervalQuality } from '@libraz/libcantus';
|
|
2127
|
+
* classifyInterval(7); // IntervalQuality.PerfectConsonance
|
|
2128
|
+
* classifyInterval(5); // IntervalQuality.Dissonance (two-voice)
|
|
2129
|
+
* classifyInterval(5, false); // IntervalQuality.ImperfectConsonance
|
|
2130
|
+
* ```
|
|
2131
|
+
* @category Pitch & Intervals
|
|
1307
2132
|
*/
|
|
1308
2133
|
declare function classifyInterval(semitones: number, twoVoice?: boolean): IntervalQuality;
|
|
1309
2134
|
/**
|
|
@@ -1311,6 +2136,7 @@ declare function classifyInterval(semitones: number, twoVoice?: boolean): Interv
|
|
|
1311
2136
|
*
|
|
1312
2137
|
* @param semitones Interval size in semitones (may be negative or compound).
|
|
1313
2138
|
* @returns True for the unison/octave (0) and the perfect fifth (7) mod 12.
|
|
2139
|
+
* @category Pitch & Intervals
|
|
1314
2140
|
*/
|
|
1315
2141
|
declare function isPerfectInterval(semitones: number): boolean;
|
|
1316
2142
|
/**
|
|
@@ -1323,6 +2149,7 @@ declare function isPerfectInterval(semitones: number): boolean;
|
|
|
1323
2149
|
* @param semitones Interval size in semitones (may be negative or compound).
|
|
1324
2150
|
* @param twoVoice When true, the perfect fourth is treated as dissonant.
|
|
1325
2151
|
* @returns True if the interval is consonant in the given context.
|
|
2152
|
+
* @category Pitch & Intervals
|
|
1326
2153
|
*/
|
|
1327
2154
|
declare function isConsonantInterval(semitones: number, twoVoice?: boolean): boolean;
|
|
1328
2155
|
|
|
@@ -1345,13 +2172,19 @@ declare function isConsonantInterval(semitones: number, twoVoice?: boolean): boo
|
|
|
1345
2172
|
* 0 natural, +1 sharp, +2 double-sharp.
|
|
1346
2173
|
* - `octave` follows scientific pitch notation (middle C = C4 = MIDI 60). When
|
|
1347
2174
|
* absent, the note denotes a bare pitch class.
|
|
2175
|
+
*
|
|
2176
|
+
* @category Pitch & Intervals
|
|
1348
2177
|
*/
|
|
1349
2178
|
type Note$1 = {
|
|
1350
2179
|
letter: number;
|
|
1351
2180
|
alter: number;
|
|
1352
2181
|
octave?: number;
|
|
1353
2182
|
};
|
|
1354
|
-
/**
|
|
2183
|
+
/**
|
|
2184
|
+
* A spelled interval: a diatonic number, a quality label, and its semitone span.
|
|
2185
|
+
*
|
|
2186
|
+
* @category Pitch & Intervals
|
|
2187
|
+
*/
|
|
1355
2188
|
type SpelledInterval = {
|
|
1356
2189
|
/** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
|
|
1357
2190
|
number: number;
|
|
@@ -1371,6 +2204,13 @@ type SpelledInterval = {
|
|
|
1371
2204
|
* @param text The note text.
|
|
1372
2205
|
* @returns The parsed note.
|
|
1373
2206
|
* @throws If the text is not a valid note.
|
|
2207
|
+
* @example
|
|
2208
|
+
* ```ts
|
|
2209
|
+
* import { parseNote, noteToMidi } from '@libraz/libcantus';
|
|
2210
|
+
* const n = parseNote('C#4');
|
|
2211
|
+
* noteToMidi(n); // 61
|
|
2212
|
+
* ```
|
|
2213
|
+
* @category Pitch & Intervals
|
|
1374
2214
|
*/
|
|
1375
2215
|
declare function parseNote(text: string): Note$1;
|
|
1376
2216
|
/**
|
|
@@ -1378,6 +2218,12 @@ declare function parseNote(text: string): Note$1;
|
|
|
1378
2218
|
*
|
|
1379
2219
|
* @param note The note to format.
|
|
1380
2220
|
* @returns The note text, including the octave when present.
|
|
2221
|
+
* @example
|
|
2222
|
+
* ```ts
|
|
2223
|
+
* import { formatNote, parseNote } from '@libraz/libcantus';
|
|
2224
|
+
* formatNote(parseNote('C#4')); // 'C#4'
|
|
2225
|
+
* ```
|
|
2226
|
+
* @category Pitch & Intervals
|
|
1381
2227
|
*/
|
|
1382
2228
|
declare function formatNote(note: Note$1): string;
|
|
1383
2229
|
/**
|
|
@@ -1385,6 +2231,12 @@ declare function formatNote(note: Note$1): string;
|
|
|
1385
2231
|
*
|
|
1386
2232
|
* @param note The note.
|
|
1387
2233
|
* @returns The pitch class.
|
|
2234
|
+
* @example
|
|
2235
|
+
* ```ts
|
|
2236
|
+
* import { noteToPitchClass, parseNote } from '@libraz/libcantus';
|
|
2237
|
+
* noteToPitchClass(parseNote('Db')); // 1
|
|
2238
|
+
* ```
|
|
2239
|
+
* @category Pitch & Intervals
|
|
1388
2240
|
*/
|
|
1389
2241
|
declare function noteToPitchClass(note: Note$1): number;
|
|
1390
2242
|
/**
|
|
@@ -1393,14 +2245,31 @@ declare function noteToPitchClass(note: Note$1): number;
|
|
|
1393
2245
|
* @param note The note; must carry an octave.
|
|
1394
2246
|
* @returns The MIDI number.
|
|
1395
2247
|
* @throws If the note has no octave.
|
|
2248
|
+
* @example
|
|
2249
|
+
* ```ts
|
|
2250
|
+
* import { noteToMidi, parseNote } from '@libraz/libcantus';
|
|
2251
|
+
* noteToMidi(parseNote('A4')); // 69
|
|
2252
|
+
* ```
|
|
2253
|
+
* @category Pitch & Intervals
|
|
1396
2254
|
*/
|
|
1397
2255
|
declare function noteToMidi(note: Note$1): number;
|
|
1398
2256
|
/**
|
|
1399
2257
|
* Name a MIDI number as a {@link Note}, choosing sharp or flat spelling.
|
|
1400
2258
|
*
|
|
2259
|
+
* The input is rounded to the nearest integer but is not clamped to the MIDI
|
|
2260
|
+
* range 0..127: out-of-range values extrapolate linearly (e.g. `-1` -> `B-2`,
|
|
2261
|
+
* `128` -> `G#9`) and remain an exact inverse of {@link noteToMidi}.
|
|
2262
|
+
*
|
|
1401
2263
|
* @param midi The MIDI number.
|
|
1402
2264
|
* @param spelling Whether to prefer sharps or flats for black keys.
|
|
1403
2265
|
* @returns The spelled note, with octave.
|
|
2266
|
+
* @example
|
|
2267
|
+
* ```ts
|
|
2268
|
+
* import { midiToNote, formatNote } from '@libraz/libcantus';
|
|
2269
|
+
* formatNote(midiToNote(61)); // 'C#4'
|
|
2270
|
+
* formatNote(midiToNote(61, 'flat')); // 'Db4'
|
|
2271
|
+
* ```
|
|
2272
|
+
* @category Pitch & Intervals
|
|
1404
2273
|
*/
|
|
1405
2274
|
declare function midiToNote(midi: number, spelling?: 'sharp' | 'flat'): Note$1;
|
|
1406
2275
|
/**
|
|
@@ -1413,10 +2282,21 @@ declare function midiToNote(midi: number, spelling?: 'sharp' | 'flat'): Note$1;
|
|
|
1413
2282
|
* @param a The lower/first note.
|
|
1414
2283
|
* @param b The upper/second note.
|
|
1415
2284
|
* @returns The diatonic number, quality, and semitone span.
|
|
2285
|
+
* @example
|
|
2286
|
+
* ```ts
|
|
2287
|
+
* import { spelledInterval, parseNote } from '@libraz/libcantus';
|
|
2288
|
+
* spelledInterval(parseNote('C4'), parseNote('G4'));
|
|
2289
|
+
* // { number: 5, quality: 'P', semitones: 7 }
|
|
2290
|
+
* ```
|
|
2291
|
+
* @category Pitch & Intervals
|
|
1416
2292
|
*/
|
|
1417
2293
|
declare function spelledInterval(a: Note$1, b: Note$1): SpelledInterval;
|
|
1418
2294
|
|
|
1419
|
-
/**
|
|
2295
|
+
/**
|
|
2296
|
+
* An inclusive MIDI pitch range for a single voice.
|
|
2297
|
+
*
|
|
2298
|
+
* @category Voicing & Counterpoint
|
|
2299
|
+
*/
|
|
1420
2300
|
type VoiceRange = {
|
|
1421
2301
|
min: number;
|
|
1422
2302
|
max: number;
|
|
@@ -1425,25 +2305,43 @@ type VoiceRange = {
|
|
|
1425
2305
|
* Default four-voice SATB ranges, ascending (index 0 = lowest):
|
|
1426
2306
|
* bass E2–C4 (40–60), tenor C3–G4 (48–67), alto G3–D5 (55–74),
|
|
1427
2307
|
* soprano C4–G5 (60–79).
|
|
2308
|
+
*
|
|
2309
|
+
* @category Voicing & Counterpoint
|
|
1428
2310
|
*/
|
|
1429
2311
|
declare const SATB_RANGES: readonly VoiceRange[];
|
|
1430
|
-
/**
|
|
2312
|
+
/**
|
|
2313
|
+
* Options controlling {@link voiceChord} and {@link voiceProgression}.
|
|
2314
|
+
*
|
|
2315
|
+
* @category Voicing & Counterpoint
|
|
2316
|
+
*/
|
|
1431
2317
|
type VoicingOptions = {
|
|
1432
|
-
/**
|
|
2318
|
+
/**
|
|
2319
|
+
* Number of voices to realize. Ignored when `ranges` is given.
|
|
2320
|
+
*
|
|
2321
|
+
* @defaultValue 4
|
|
2322
|
+
*/
|
|
1433
2323
|
voices?: number;
|
|
1434
2324
|
/** Explicit per-voice ranges, ascending (index 0 = lowest). Takes precedence over `voices`. */
|
|
1435
2325
|
ranges?: VoiceRange[];
|
|
1436
|
-
/**
|
|
2326
|
+
/**
|
|
2327
|
+
* Maximum spacing in semitones between adjacent upper voices.
|
|
2328
|
+
*
|
|
2329
|
+
* @defaultValue 12
|
|
2330
|
+
*/
|
|
1437
2331
|
maxSpacing?: number;
|
|
1438
2332
|
};
|
|
1439
2333
|
/**
|
|
1440
2334
|
* Total voice-leading cost between two voicings: the sum of absolute semitone
|
|
1441
|
-
* motion across voices
|
|
1442
|
-
*
|
|
2335
|
+
* motion across voices, plus a moderate {@link HIDDEN_PERFECT_PENALTY} when the
|
|
2336
|
+
* outer-voice (bass–soprano) pair reaches a hidden/direct perfect fifth or
|
|
2337
|
+
* octave by similar motion. The arrays must be the same length; when they
|
|
2338
|
+
* differ the voicings are not comparable and the cost is `Infinity`.
|
|
1443
2339
|
*
|
|
1444
2340
|
* @param from The previous voicing, one MIDI pitch per voice.
|
|
1445
2341
|
* @param to The next voicing, one MIDI pitch per voice.
|
|
1446
|
-
* @returns The summed absolute motion
|
|
2342
|
+
* @returns The summed absolute motion (plus any hidden-perfect penalty), or
|
|
2343
|
+
* `Infinity` when lengths differ.
|
|
2344
|
+
* @category Voicing & Counterpoint
|
|
1447
2345
|
*/
|
|
1448
2346
|
declare function voiceLeadingCost(from: number[], to: number[]): number;
|
|
1449
2347
|
/**
|
|
@@ -1458,6 +2356,13 @@ declare function voiceLeadingCost(from: number[], to: number[]): number;
|
|
|
1458
2356
|
* @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
|
|
1459
2357
|
* @returns MIDI pitches, ascending, one per voice.
|
|
1460
2358
|
* @throws If no voicing fits the given ranges.
|
|
2359
|
+
* @example
|
|
2360
|
+
* ```ts
|
|
2361
|
+
* import { parseChordSymbol, voiceChord } from '@libraz/libcantus';
|
|
2362
|
+
* const chord = parseChordSymbol('Cmaj7');
|
|
2363
|
+
* voiceChord(chord); // four ascending MIDI pitches within the SATB ranges
|
|
2364
|
+
* ```
|
|
2365
|
+
* @category Voicing & Counterpoint
|
|
1461
2366
|
*/
|
|
1462
2367
|
declare function voiceChord(chord: Chord$1, opts?: VoicingOptions): number[];
|
|
1463
2368
|
/**
|
|
@@ -1472,6 +2377,13 @@ declare function voiceChord(chord: Chord$1, opts?: VoicingOptions): number[];
|
|
|
1472
2377
|
* @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
|
|
1473
2378
|
* @returns One voicing per chord, each ascending with one MIDI pitch per voice.
|
|
1474
2379
|
* @throws If any chord admits no voicing within the given ranges.
|
|
2380
|
+
* @example
|
|
2381
|
+
* ```ts
|
|
2382
|
+
* import { parseChordSymbol, voiceProgression } from '@libraz/libcantus';
|
|
2383
|
+
* const chords = ['C', 'Am', 'F', 'G'].map((s) => parseChordSymbol(s));
|
|
2384
|
+
* voiceProgression(chords); // one four-voice voicing per chord, smoothly led
|
|
2385
|
+
* ```
|
|
2386
|
+
* @category Voicing & Counterpoint
|
|
1475
2387
|
*/
|
|
1476
2388
|
declare function voiceProgression(chords: Chord$1[], opts?: VoicingOptions): number[][];
|
|
1477
2389
|
/**
|
|
@@ -1484,17 +2396,35 @@ declare function voiceProgression(chords: Chord$1[], opts?: VoicingOptions): num
|
|
|
1484
2396
|
* root/third/fifth for triads; the fifth and tensions are omitted.
|
|
1485
2397
|
* - `rootless`: the root omitted, keeping third/fifth/seventh and tensions
|
|
1486
2398
|
* (a typical left-hand jazz voicing).
|
|
2399
|
+
*
|
|
2400
|
+
* @category Voicing & Counterpoint
|
|
1487
2401
|
*/
|
|
1488
2402
|
type VoicingStyle = 'close' | 'drop2' | 'drop3' | 'shell' | 'rootless';
|
|
1489
|
-
/**
|
|
2403
|
+
/**
|
|
2404
|
+
* Options controlling {@link voiceChordStyled}.
|
|
2405
|
+
*
|
|
2406
|
+
* @category Voicing & Counterpoint
|
|
2407
|
+
*/
|
|
1490
2408
|
type StyledVoicingOptions = {
|
|
1491
|
-
/**
|
|
2409
|
+
/**
|
|
2410
|
+
* Voicing style to build.
|
|
2411
|
+
*
|
|
2412
|
+
* @defaultValue 'close'
|
|
2413
|
+
*/
|
|
1492
2414
|
style?: VoicingStyle;
|
|
1493
2415
|
/** Constrain the highest voice to this pitch class (0..11) when given. */
|
|
1494
2416
|
topNote?: number;
|
|
1495
|
-
/**
|
|
2417
|
+
/**
|
|
2418
|
+
* Base octave for the close stack; the stack begins near `12 * octave`.
|
|
2419
|
+
*
|
|
2420
|
+
* @defaultValue 4
|
|
2421
|
+
*/
|
|
1496
2422
|
octave?: number;
|
|
1497
|
-
/**
|
|
2423
|
+
/**
|
|
2424
|
+
* Omit the root regardless of style (relevant for jazz voicings).
|
|
2425
|
+
*
|
|
2426
|
+
* @defaultValue false
|
|
2427
|
+
*/
|
|
1498
2428
|
rootless?: boolean;
|
|
1499
2429
|
};
|
|
1500
2430
|
/**
|
|
@@ -1512,6 +2442,13 @@ type StyledVoicingOptions = {
|
|
|
1512
2442
|
* @param chord The chord to voice.
|
|
1513
2443
|
* @param opts Styled voicing options; defaults to a close voicing at octave 4.
|
|
1514
2444
|
* @returns MIDI pitches, ascending, one per retained voice.
|
|
2445
|
+
* @example
|
|
2446
|
+
* ```ts
|
|
2447
|
+
* import { parseChordSymbol, voiceChordStyled } from '@libraz/libcantus';
|
|
2448
|
+
* const chord = parseChordSymbol('Dm7');
|
|
2449
|
+
* voiceChordStyled(chord, { style: 'drop2' }); // ascending MIDI pitches, drop-2 voicing
|
|
2450
|
+
* ```
|
|
2451
|
+
* @category Voicing & Counterpoint
|
|
1515
2452
|
*/
|
|
1516
2453
|
declare function voiceChordStyled(chord: Chord$1, opts?: StyledVoicingOptions): number[];
|
|
1517
2454
|
/**
|
|
@@ -1529,6 +2466,7 @@ declare function voiceChordStyled(chord: Chord$1, opts?: StyledVoicingOptions):
|
|
|
1529
2466
|
* @param opts Voicing options; when omitted, ranges follow `current`'s span.
|
|
1530
2467
|
* @returns The chosen voicing, ascending with one MIDI pitch per voice.
|
|
1531
2468
|
* @throws If no voicing fits the derived ranges.
|
|
2469
|
+
* @category Voicing & Counterpoint
|
|
1532
2470
|
*/
|
|
1533
2471
|
declare function nextVoicing(current: number[], chord: Chord$1, opts?: VoicingOptions): number[];
|
|
1534
2472
|
|
|
@@ -1546,6 +2484,13 @@ declare function nextVoicing(current: number[], chord: Chord$1, opts?: VoicingOp
|
|
|
1546
2484
|
* An immutable spelled note: a diatonic letter plus a chromatic alteration and
|
|
1547
2485
|
* an optional octave. Wraps the plain note object and delegates to the pitch
|
|
1548
2486
|
* module; every transformation returns a new instance.
|
|
2487
|
+
*
|
|
2488
|
+
* @category Class API
|
|
2489
|
+
* @example
|
|
2490
|
+
* ```ts
|
|
2491
|
+
* import { Note } from '@libraz/libcantus';
|
|
2492
|
+
* Note.of('C4').transpose(7).name; // 'G4'
|
|
2493
|
+
* ```
|
|
1549
2494
|
*/
|
|
1550
2495
|
declare class Note {
|
|
1551
2496
|
#private;
|
|
@@ -1601,7 +2546,8 @@ declare class Note {
|
|
|
1601
2546
|
*
|
|
1602
2547
|
* When the note carries an octave the transposition happens in MIDI space and
|
|
1603
2548
|
* the result is spelled with a sharp preference. An octave-less note stays
|
|
1604
|
-
* octave-less: only its pitch class is moved.
|
|
2549
|
+
* octave-less: only its pitch class is moved. Transposing by zero is the
|
|
2550
|
+
* identity: the original spelling is preserved (no enharmonic respelling).
|
|
1605
2551
|
*
|
|
1606
2552
|
* @param semitones The signed semitone offset.
|
|
1607
2553
|
* @returns The transposed note.
|
|
@@ -1621,11 +2567,27 @@ declare class Note {
|
|
|
1621
2567
|
* @returns True if the spellings are identical.
|
|
1622
2568
|
*/
|
|
1623
2569
|
equals(other: Note): boolean;
|
|
2570
|
+
/**
|
|
2571
|
+
* The plain note data, for JSON serialization.
|
|
2572
|
+
*
|
|
2573
|
+
* Private class fields do not serialize, so an explicit `toJSON` keeps
|
|
2574
|
+
* `JSON.stringify(note)` from collapsing to `{}`.
|
|
2575
|
+
*
|
|
2576
|
+
* @returns A copy of the underlying plain note object.
|
|
2577
|
+
*/
|
|
2578
|
+
toJSON(): Note$1;
|
|
1624
2579
|
}
|
|
1625
2580
|
/**
|
|
1626
2581
|
* An immutable spelled interval value: a diatonic number, a quality label, and
|
|
1627
2582
|
* a signed semitone span. A thin convenience wrapper over the pitch module's
|
|
1628
2583
|
* plain interval result.
|
|
2584
|
+
*
|
|
2585
|
+
* @category Class API
|
|
2586
|
+
* @example
|
|
2587
|
+
* ```ts
|
|
2588
|
+
* import { Interval, Note } from '@libraz/libcantus';
|
|
2589
|
+
* Interval.between(Note.of('C4'), Note.of('G4')).name; // 'P5'
|
|
2590
|
+
* ```
|
|
1629
2591
|
*/
|
|
1630
2592
|
declare class Interval {
|
|
1631
2593
|
#private;
|
|
@@ -1655,11 +2617,27 @@ declare class Interval {
|
|
|
1655
2617
|
get semitones(): number;
|
|
1656
2618
|
/** A readable label composed of quality and number, e.g. `'P5'` or `'M3'`. */
|
|
1657
2619
|
get name(): string;
|
|
2620
|
+
/**
|
|
2621
|
+
* The plain interval data, for JSON serialization.
|
|
2622
|
+
*
|
|
2623
|
+
* Private class fields do not serialize, so an explicit `toJSON` keeps
|
|
2624
|
+
* `JSON.stringify(interval)` from collapsing to `{}`.
|
|
2625
|
+
*
|
|
2626
|
+
* @returns The diatonic number, quality, and semitone span.
|
|
2627
|
+
*/
|
|
2628
|
+
toJSON(): SpelledInterval;
|
|
1658
2629
|
}
|
|
1659
2630
|
/**
|
|
1660
2631
|
* An immutable key/scale: a `KeyScale` (root pitch class plus mode mask) paired
|
|
1661
2632
|
* with a spelled tonic that anchors letter-name spelling. Acts as the factory
|
|
1662
2633
|
* for key-aware chords.
|
|
2634
|
+
*
|
|
2635
|
+
* @category Class API
|
|
2636
|
+
* @example
|
|
2637
|
+
* ```ts
|
|
2638
|
+
* import { Key } from '@libraz/libcantus';
|
|
2639
|
+
* Key.major('C').chord(4).symbol(); // 'G' (the diatonic triad on scale degree 4)
|
|
2640
|
+
* ```
|
|
1663
2641
|
*/
|
|
1664
2642
|
declare class Key {
|
|
1665
2643
|
#private;
|
|
@@ -1673,16 +2651,18 @@ declare class Key {
|
|
|
1673
2651
|
/**
|
|
1674
2652
|
* A major key.
|
|
1675
2653
|
*
|
|
1676
|
-
* @param root Tonic as a note name (e.g. `'Eb'`) or a pitch class; numeric
|
|
1677
|
-
*
|
|
2654
|
+
* @param root Tonic as a note name (e.g. `'Eb'`) or a pitch class; a numeric
|
|
2655
|
+
* root is spelled with whichever accidental side yields the fewest
|
|
2656
|
+
* accidentals across the scale.
|
|
1678
2657
|
* @returns The major key.
|
|
1679
2658
|
*/
|
|
1680
2659
|
static major(root: string | number): Key;
|
|
1681
2660
|
/**
|
|
1682
2661
|
* A natural-minor key.
|
|
1683
2662
|
*
|
|
1684
|
-
* @param root Tonic as a note name or a pitch class; numeric
|
|
1685
|
-
* spelled with
|
|
2663
|
+
* @param root Tonic as a note name or a pitch class; a numeric root is
|
|
2664
|
+
* spelled with whichever accidental side yields the fewest accidentals
|
|
2665
|
+
* across the scale.
|
|
1686
2666
|
* @returns The minor key.
|
|
1687
2667
|
*/
|
|
1688
2668
|
static minor(root: string | number): Key;
|
|
@@ -1778,12 +2758,33 @@ declare class Key {
|
|
|
1778
2758
|
* @returns True if the pitch class is a scale tone.
|
|
1779
2759
|
*/
|
|
1780
2760
|
contains(x: number | Note): boolean;
|
|
2761
|
+
/**
|
|
2762
|
+
* The plain key data, for JSON serialization.
|
|
2763
|
+
*
|
|
2764
|
+
* Private class fields do not serialize, so an explicit `toJSON` keeps
|
|
2765
|
+
* `JSON.stringify(key)` from collapsing to `{}`. The result pairs the
|
|
2766
|
+
* `KeyScale` with the spelled tonic, enough to reconstruct the key via
|
|
2767
|
+
* {@link Key.of}.
|
|
2768
|
+
*
|
|
2769
|
+
* @returns The key/scale and its spelled tonic.
|
|
2770
|
+
*/
|
|
2771
|
+
toJSON(): {
|
|
2772
|
+
scale: KeyScale;
|
|
2773
|
+
tonic: Note$1;
|
|
2774
|
+
};
|
|
1781
2775
|
}
|
|
1782
2776
|
/**
|
|
1783
2777
|
* An immutable chord: a root pitch class, quality, interval template, and
|
|
1784
2778
|
* optional slash bass, optionally carrying a {@link Key} context. Analysis
|
|
1785
2779
|
* methods (`roman`, `function`, `analyze`, ...) use an explicitly passed key
|
|
1786
2780
|
* first and fall back to the carried context.
|
|
2781
|
+
*
|
|
2782
|
+
* @category Class API
|
|
2783
|
+
* @example
|
|
2784
|
+
* ```ts
|
|
2785
|
+
* import { Chord } from '@libraz/libcantus';
|
|
2786
|
+
* Chord.parse('Cmaj7').invert(1).symbol(); // 'Cmaj7/E' (third in the bass)
|
|
2787
|
+
* ```
|
|
1787
2788
|
*/
|
|
1788
2789
|
declare class Chord {
|
|
1789
2790
|
#private;
|
|
@@ -1937,6 +2938,9 @@ declare class Chord {
|
|
|
1937
2938
|
* root in the interval template (`invert(1)` puts the third in the bass).
|
|
1938
2939
|
* `n` wraps around the template length; negative values count backwards.
|
|
1939
2940
|
*
|
|
2941
|
+
* `invert(0)` (and any `n` that wraps to it) is root position, so it carries
|
|
2942
|
+
* no slash bass and equals the original chord.
|
|
2943
|
+
*
|
|
1940
2944
|
* @param n The inversion number.
|
|
1941
2945
|
* @returns The inverted chord, keeping any key context.
|
|
1942
2946
|
* @throws If the chord has no intervals.
|
|
@@ -2000,6 +3004,15 @@ declare class Chord {
|
|
|
2000
3004
|
/**
|
|
2001
3005
|
* An immutable ordered sequence of chords, optionally carrying a {@link Key}
|
|
2002
3006
|
* context shared by its analysis methods.
|
|
3007
|
+
*
|
|
3008
|
+
* @category Class API
|
|
3009
|
+
* @example
|
|
3010
|
+
* ```ts
|
|
3011
|
+
* import { Key } from '@libraz/libcantus';
|
|
3012
|
+
* const key = Key.major('C');
|
|
3013
|
+
* key.chord(1).progressionTo(key.chord(4), key.chord(0)).roman();
|
|
3014
|
+
* // ['ii', 'V', 'I']
|
|
3015
|
+
* ```
|
|
2003
3016
|
*/
|
|
2004
3017
|
declare class Progression {
|
|
2005
3018
|
#private;
|
|
@@ -2068,28 +3081,83 @@ declare class Progression {
|
|
|
2068
3081
|
chords: ChordAnalysis[];
|
|
2069
3082
|
cadence: Cadence;
|
|
2070
3083
|
};
|
|
3084
|
+
/**
|
|
3085
|
+
* The plain progression data, for JSON serialization.
|
|
3086
|
+
*
|
|
3087
|
+
* Private class fields do not serialize, so an explicit `toJSON` keeps
|
|
3088
|
+
* `JSON.stringify(progression)` from collapsing to `{}`. The chords are
|
|
3089
|
+
* emitted as plain data and the carried key, when present, as its own data.
|
|
3090
|
+
*
|
|
3091
|
+
* @returns The chord data sequence and the carried key, if any.
|
|
3092
|
+
*/
|
|
3093
|
+
toJSON(): {
|
|
3094
|
+
chords: Chord$1[];
|
|
3095
|
+
key: {
|
|
3096
|
+
scale: KeyScale;
|
|
3097
|
+
tonic: Note$1;
|
|
3098
|
+
} | undefined;
|
|
3099
|
+
};
|
|
2071
3100
|
}
|
|
2072
3101
|
|
|
2073
|
-
/**
|
|
3102
|
+
/**
|
|
3103
|
+
* A transformation applicable to a motif cell.
|
|
3104
|
+
*
|
|
3105
|
+
* @category Composition
|
|
3106
|
+
*/
|
|
2074
3107
|
type MotifTransform = 'transposeDiatonic' | 'transposeChromatic' | 'invert' | 'retrograde' | 'augment' | 'diminish' | 'sequence';
|
|
2075
|
-
/**
|
|
3108
|
+
/**
|
|
3109
|
+
* A single note within a motif cell.
|
|
3110
|
+
*
|
|
3111
|
+
* @category Composition
|
|
3112
|
+
*/
|
|
2076
3113
|
type MotifNote = {
|
|
2077
3114
|
pitch: number;
|
|
2078
3115
|
startBeat: number;
|
|
2079
3116
|
durationBeat: number;
|
|
2080
3117
|
};
|
|
2081
|
-
/**
|
|
3118
|
+
/**
|
|
3119
|
+
* A short melodic cell.
|
|
3120
|
+
*
|
|
3121
|
+
* @category Composition
|
|
3122
|
+
*/
|
|
2082
3123
|
type MotifCell = {
|
|
2083
3124
|
notes: MotifNote[];
|
|
2084
3125
|
};
|
|
2085
|
-
/**
|
|
3126
|
+
/**
|
|
3127
|
+
* Melodic contour shape for {@link generateMotif}.
|
|
3128
|
+
*
|
|
3129
|
+
* @category Composition
|
|
3130
|
+
*/
|
|
2086
3131
|
type MotifContour = 'arch' | 'ascending' | 'descending' | 'wave';
|
|
2087
|
-
/**
|
|
3132
|
+
/**
|
|
3133
|
+
* Options controlling {@link generateMotif}.
|
|
3134
|
+
*
|
|
3135
|
+
* @category Composition
|
|
3136
|
+
*/
|
|
2088
3137
|
type GenerateMotifOptions = {
|
|
2089
3138
|
key: KeyScale;
|
|
2090
3139
|
chord?: Chord$1 | null;
|
|
2091
3140
|
bars: number;
|
|
3141
|
+
/**
|
|
3142
|
+
* Melodic contour shape the line follows.
|
|
3143
|
+
*
|
|
3144
|
+
* @defaultValue `'arch'`
|
|
3145
|
+
*/
|
|
2092
3146
|
contour?: MotifContour;
|
|
3147
|
+
/**
|
|
3148
|
+
* Probability in [0, 1] that a note is nudged by a single diatonic step. The
|
|
3149
|
+
* nudge direction is balanced (up or down with equal odds), so it adds
|
|
3150
|
+
* variety without biasing the line off its contour. Default 0, which
|
|
3151
|
+
* reproduces the requested contour exactly (no drift, tails return to tonic).
|
|
3152
|
+
*
|
|
3153
|
+
* @defaultValue 0
|
|
3154
|
+
*/
|
|
3155
|
+
jitter?: number;
|
|
3156
|
+
/**
|
|
3157
|
+
* Seed for the deterministic PRNG.
|
|
3158
|
+
*
|
|
3159
|
+
* @defaultValue 0
|
|
3160
|
+
*/
|
|
2093
3161
|
seed?: number;
|
|
2094
3162
|
};
|
|
2095
3163
|
/**
|
|
@@ -2097,10 +3165,21 @@ type GenerateMotifOptions = {
|
|
|
2097
3165
|
*
|
|
2098
3166
|
* The melody follows the requested contour in diatonic steps from the tonic,
|
|
2099
3167
|
* snapped to the key. When a chord is supplied, notes landing on bar downbeats
|
|
2100
|
-
* are pulled to the nearest chord tone.
|
|
3168
|
+
* are pulled to the nearest chord tone. An optional `jitter` adds balanced
|
|
3169
|
+
* per-note variation without biasing the contour; it is off by default, so the
|
|
3170
|
+
* contour is reproduced exactly. Output is deterministic for a given seed.
|
|
2101
3171
|
*
|
|
2102
|
-
* @param opts Key, optional chord, length, contour, and seed.
|
|
3172
|
+
* @param opts Key, optional chord, length, contour, jitter, and seed.
|
|
2103
3173
|
* @returns The generated motif cell.
|
|
3174
|
+
*
|
|
3175
|
+
* @example
|
|
3176
|
+
* ```ts
|
|
3177
|
+
* import { generateMotif, majorKey } from '@libraz/libcantus';
|
|
3178
|
+
* const cell = generateMotif({ key: majorKey(0), bars: 2, contour: 'arch' });
|
|
3179
|
+
* // cell.notes is a deterministic MotifCell (seed defaults to 0)
|
|
3180
|
+
* ```
|
|
3181
|
+
*
|
|
3182
|
+
* @category Composition
|
|
2104
3183
|
*/
|
|
2105
3184
|
declare function generateMotif(opts: GenerateMotifOptions): MotifCell;
|
|
2106
3185
|
/**
|
|
@@ -2121,6 +3200,15 @@ declare function generateMotif(opts: GenerateMotifOptions): MotifCell;
|
|
|
2121
3200
|
* @param key Key context for the diatonic transforms; without it,
|
|
2122
3201
|
* `transposeDiatonic` and `sequence` shift chromatically by semitones.
|
|
2123
3202
|
* @returns The transformed cell.
|
|
3203
|
+
*
|
|
3204
|
+
* @example
|
|
3205
|
+
* ```ts
|
|
3206
|
+
* import { generateMotif, transformMotif, majorKey } from '@libraz/libcantus';
|
|
3207
|
+
* const cell = generateMotif({ key: majorKey(0), bars: 1 });
|
|
3208
|
+
* const inverted = transformMotif(cell, 'invert');
|
|
3209
|
+
* ```
|
|
3210
|
+
*
|
|
3211
|
+
* @category Composition
|
|
2124
3212
|
*/
|
|
2125
3213
|
declare function transformMotif(cell: MotifCell, t: MotifTransform, amount?: number, key?: KeyScale): MotifCell;
|
|
2126
3214
|
/**
|
|
@@ -2135,6 +3223,16 @@ declare function transformMotif(cell: MotifCell, t: MotifTransform, amount?: num
|
|
|
2135
3223
|
* @param key Key context (used to keep snapped pitches sensible).
|
|
2136
3224
|
* @param bars Number of four-beat bars to fill.
|
|
2137
3225
|
* @returns The developed, harmony-aware cell.
|
|
3226
|
+
*
|
|
3227
|
+
* @example
|
|
3228
|
+
* ```ts
|
|
3229
|
+
* import { chordTimelineFromChords, developMotif, generateMotif, majorKey } from '@libraz/libcantus';
|
|
3230
|
+
* const key = majorKey(0);
|
|
3231
|
+
* const timeline = chordTimelineFromChords([{ rootPc: 0, quality: 'maj', startBeat: 0 }], 8);
|
|
3232
|
+
* const developed = developMotif(generateMotif({ key, bars: 1 }), timeline, key, 2);
|
|
3233
|
+
* ```
|
|
3234
|
+
*
|
|
3235
|
+
* @category Composition
|
|
2138
3236
|
*/
|
|
2139
3237
|
declare function developMotif(cell: MotifCell, timeline: ChordTimeline, key: KeyScale, bars: number): MotifCell;
|
|
2140
3238
|
|
|
@@ -2144,7 +3242,11 @@ declare function developMotif(cell: MotifCell, timeline: ChordTimeline, key: Key
|
|
|
2144
3242
|
* mulberry32 core exposes the small set of samplers those modules need; given a
|
|
2145
3243
|
* seed the stream is fully reproducible.
|
|
2146
3244
|
*/
|
|
2147
|
-
/**
|
|
3245
|
+
/**
|
|
3246
|
+
* A deterministic PRNG with the sampling helpers the generators need.
|
|
3247
|
+
*
|
|
3248
|
+
* @category Utilities
|
|
3249
|
+
*/
|
|
2148
3250
|
type Rng = {
|
|
2149
3251
|
/** Next float in [0, 1). */
|
|
2150
3252
|
next: () => number;
|
|
@@ -2160,6 +3262,14 @@ type Rng = {
|
|
|
2160
3262
|
*
|
|
2161
3263
|
* @param seed The 32-bit seed; the same seed always yields the same stream.
|
|
2162
3264
|
* @returns The seeded generator.
|
|
3265
|
+
* @example
|
|
3266
|
+
* ```ts
|
|
3267
|
+
* import { createRng } from '@libraz/libcantus';
|
|
3268
|
+
* const rng = createRng(42);
|
|
3269
|
+
* rng.next(); // deterministic float in [0, 1)
|
|
3270
|
+
* rng.range(1, 6); // deterministic integer in [1, 6]
|
|
3271
|
+
* ```
|
|
3272
|
+
* @category Utilities
|
|
2163
3273
|
*/
|
|
2164
3274
|
declare function createRng(seed: number): Rng;
|
|
2165
3275
|
|
|
@@ -2172,16 +3282,28 @@ declare function createRng(seed: number): Rng;
|
|
|
2172
3282
|
* Roots are pitch classes (0..11); no spelled key signature is required.
|
|
2173
3283
|
*/
|
|
2174
3284
|
|
|
2175
|
-
/**
|
|
3285
|
+
/**
|
|
3286
|
+
* The kind of substitution relationship a candidate realizes.
|
|
3287
|
+
*
|
|
3288
|
+
* @category Reharmonization
|
|
3289
|
+
*/
|
|
2176
3290
|
type SubstitutionType = 'tritone' | 'relative' | 'borrowed' | 'chromatic-mediant' | 'secondary-dominant';
|
|
2177
|
-
/**
|
|
3291
|
+
/**
|
|
3292
|
+
* A proposed chord substitution with its relationship, numeral, and function.
|
|
3293
|
+
*
|
|
3294
|
+
* @category Reharmonization
|
|
3295
|
+
*/
|
|
2178
3296
|
type Substitution = {
|
|
2179
3297
|
chord: Chord$1;
|
|
2180
3298
|
type: SubstitutionType;
|
|
2181
3299
|
roman: string;
|
|
2182
3300
|
function: HarmonicFunction;
|
|
2183
3301
|
};
|
|
2184
|
-
/**
|
|
3302
|
+
/**
|
|
3303
|
+
* Options controlling which substitutions {@link substituteChord} returns.
|
|
3304
|
+
*
|
|
3305
|
+
* @category Reharmonization
|
|
3306
|
+
*/
|
|
2185
3307
|
type SubstituteOptions = {
|
|
2186
3308
|
/**
|
|
2187
3309
|
* Melody pitch classes that must remain chord tones. When given, only
|
|
@@ -2212,9 +3334,20 @@ type SubstituteOptions = {
|
|
|
2212
3334
|
* @param key The prevailing key.
|
|
2213
3335
|
* @param opts Optional melody-preservation constraint.
|
|
2214
3336
|
* @returns The deduplicated substitution candidates.
|
|
3337
|
+
* @example
|
|
3338
|
+
* ```ts
|
|
3339
|
+
* import { substituteChord, makeChord, majorKey } from '@libraz/libcantus';
|
|
3340
|
+
* const subs = substituteChord(makeChord(7, 'dom7'), majorKey(0));
|
|
3341
|
+
* // includes the tritone sub Db7 (rootPc 1, quality 'dom7')
|
|
3342
|
+
* ```
|
|
3343
|
+
* @category Reharmonization
|
|
2215
3344
|
*/
|
|
2216
3345
|
declare function substituteChord(chord: Chord$1, key: KeyScale, opts?: SubstituteOptions): Substitution[];
|
|
2217
|
-
/**
|
|
3346
|
+
/**
|
|
3347
|
+
* A borrowed chord in the modal-interchange palette of a key.
|
|
3348
|
+
*
|
|
3349
|
+
* @category Reharmonization
|
|
3350
|
+
*/
|
|
2218
3351
|
type BorrowedChord = {
|
|
2219
3352
|
chord: Chord$1;
|
|
2220
3353
|
roman: string;
|
|
@@ -2230,6 +3363,7 @@ type BorrowedChord = {
|
|
|
2230
3363
|
*
|
|
2231
3364
|
* @param key The prevailing key.
|
|
2232
3365
|
* @returns The borrowed-chord palette.
|
|
3366
|
+
* @category Reharmonization
|
|
2233
3367
|
*/
|
|
2234
3368
|
declare function modalInterchangePalette(key: KeyScale): BorrowedChord[];
|
|
2235
3369
|
/**
|
|
@@ -2244,6 +3378,13 @@ declare function modalInterchangePalette(key: KeyScale): BorrowedChord[];
|
|
|
2244
3378
|
* @param chord The chord to reflect.
|
|
2245
3379
|
* @param key The prevailing key.
|
|
2246
3380
|
* @returns The negative-harmony counterpart of the chord.
|
|
3381
|
+
* @example
|
|
3382
|
+
* ```ts
|
|
3383
|
+
* import { negativeHarmonyMirror, makeChord, majorKey } from '@libraz/libcantus';
|
|
3384
|
+
* negativeHarmonyMirror(makeChord(7, 'maj'), majorKey(0));
|
|
3385
|
+
* // G major reflected across C's axis => F minor (rootPc 5, quality 'min')
|
|
3386
|
+
* ```
|
|
3387
|
+
* @category Reharmonization
|
|
2247
3388
|
*/
|
|
2248
3389
|
declare function negativeHarmonyMirror(chord: Chord$1, key: KeyScale): Chord$1;
|
|
2249
3390
|
|
|
@@ -2260,28 +3401,49 @@ declare function negativeHarmonyMirror(chord: Chord$1, key: KeyScale): Chord$1;
|
|
|
2260
3401
|
* library-wide convention.
|
|
2261
3402
|
*/
|
|
2262
3403
|
|
|
2263
|
-
/**
|
|
3404
|
+
/**
|
|
3405
|
+
* A single rhythmic note: an onset position and how long it sounds.
|
|
3406
|
+
*
|
|
3407
|
+
* @category Rhythm & Meter
|
|
3408
|
+
*/
|
|
2264
3409
|
type RhythmEvent = {
|
|
2265
3410
|
/** Onset position in quarter-note beats, absolute from the start of the span. */
|
|
2266
3411
|
position: number;
|
|
2267
3412
|
/** Duration in quarter-note beats, extending to the next onset. */
|
|
2268
3413
|
duration: number;
|
|
2269
3414
|
};
|
|
2270
|
-
/**
|
|
3415
|
+
/**
|
|
3416
|
+
* Options controlling {@link generateRhythm}.
|
|
3417
|
+
*
|
|
3418
|
+
* @category Rhythm & Meter
|
|
3419
|
+
*/
|
|
2271
3420
|
type RhythmOptions = {
|
|
2272
|
-
/**
|
|
3421
|
+
/**
|
|
3422
|
+
* Seed for the deterministic PRNG.
|
|
3423
|
+
*
|
|
3424
|
+
* @defaultValue 0
|
|
3425
|
+
*/
|
|
2273
3426
|
seed?: number;
|
|
2274
|
-
/**
|
|
3427
|
+
/**
|
|
3428
|
+
* Number of bars to generate.
|
|
3429
|
+
*
|
|
3430
|
+
* @defaultValue 1
|
|
3431
|
+
*/
|
|
2275
3432
|
bars?: number;
|
|
2276
3433
|
/**
|
|
2277
3434
|
* Grid resolution as the number of equal grid steps per quarter-note beat.
|
|
2278
3435
|
* The default of 2 yields an eighth-note grid; 4 yields a sixteenth-note
|
|
2279
3436
|
* grid, 3 an eighth-note triplet grid, and so on.
|
|
3437
|
+
*
|
|
3438
|
+
* @defaultValue 2
|
|
2280
3439
|
*/
|
|
2281
3440
|
subdivision?: number;
|
|
2282
3441
|
/**
|
|
2283
3442
|
* Overall onset density in [0, 1]. Scales the per-slot onset probability, so
|
|
2284
|
-
* higher values fill more grid slots.
|
|
3443
|
+
* higher values fill more grid slots. Values outside [0, 1] are clamped into
|
|
3444
|
+
* range.
|
|
3445
|
+
*
|
|
3446
|
+
* @defaultValue 0.5
|
|
2285
3447
|
*/
|
|
2286
3448
|
density?: number;
|
|
2287
3449
|
};
|
|
@@ -2293,19 +3455,28 @@ type RhythmOptions = {
|
|
|
2293
3455
|
*
|
|
2294
3456
|
* @param weight The metric weight, 0 (off-pulse) to 3 (downbeat).
|
|
2295
3457
|
* @returns A base onset probability in [0, 1].
|
|
3458
|
+
* @category Rhythm & Meter
|
|
2296
3459
|
*/
|
|
2297
3460
|
declare function onsetWeightCurve(weight: number): number;
|
|
2298
3461
|
/**
|
|
2299
3462
|
* Generate a deterministic rhythmic pattern over `bars` bars of a time
|
|
2300
3463
|
* signature. A grid is built at the chosen subdivision; each slot becomes an
|
|
2301
3464
|
* onset with a probability derived from its metric weight scaled by `density`
|
|
2302
|
-
* and sampled from the seeded PRNG.
|
|
2303
|
-
*
|
|
2304
|
-
* extends to the
|
|
3465
|
+
* and sampled from the seeded PRNG. Every bar's downbeat is always an onset, so
|
|
3466
|
+
* a pattern of any length has a reliable pulse independent of the seed. Each
|
|
3467
|
+
* event's duration extends to the next onset, and the last event extends to the
|
|
3468
|
+
* end of the span. `density` is clamped to [0, 1].
|
|
2305
3469
|
*
|
|
2306
3470
|
* @param ts The time signature.
|
|
2307
3471
|
* @param opts Generation options.
|
|
2308
3472
|
* @returns Onset events sorted by position, non-overlapping, covering the span.
|
|
3473
|
+
* @example
|
|
3474
|
+
* ```ts
|
|
3475
|
+
* import { parseTimeSignature, generateRhythm } from '@libraz/libcantus';
|
|
3476
|
+
* const ts = parseTimeSignature('4/4');
|
|
3477
|
+
* generateRhythm(ts, { seed: 42, density: 0.6 }); // onset events over one bar
|
|
3478
|
+
* ```
|
|
3479
|
+
* @category Rhythm & Meter
|
|
2309
3480
|
*/
|
|
2310
3481
|
declare function generateRhythm(ts: TimeSignature, opts?: RhythmOptions): RhythmEvent[];
|
|
2311
3482
|
/**
|
|
@@ -2314,6 +3485,7 @@ declare function generateRhythm(ts: TimeSignature, opts?: RhythmOptions): Rhythm
|
|
|
2314
3485
|
* @param events Events from {@link generateRhythm}.
|
|
2315
3486
|
* @param ts The time signature.
|
|
2316
3487
|
* @returns The onset count divided by the number of bars the events span.
|
|
3488
|
+
* @category Rhythm & Meter
|
|
2317
3489
|
*/
|
|
2318
3490
|
declare function rhythmDensity(events: RhythmEvent[], ts: TimeSignature): number;
|
|
2319
3491
|
|
|
@@ -2322,45 +3494,135 @@ declare function rhythmDensity(events: RhythmEvent[], ts: TimeSignature): number
|
|
|
2322
3494
|
*
|
|
2323
3495
|
* Bit 0 (the root) is always set, enforcing the `KeyScale` invariant that the
|
|
2324
3496
|
* root is a scale tone even when the offset list omits 0.
|
|
3497
|
+
*
|
|
3498
|
+
* @category Scales
|
|
2325
3499
|
*/
|
|
2326
3500
|
declare function maskFromOffsets(offsets: readonly number[]): number;
|
|
2327
|
-
/**
|
|
3501
|
+
/**
|
|
3502
|
+
* Mode mask for the major (Ionian) scale: offsets {0, 2, 4, 5, 7, 9, 11}.
|
|
3503
|
+
*
|
|
3504
|
+
* @category Scales
|
|
3505
|
+
*/
|
|
2328
3506
|
declare const MAJOR_MASK = 2741;
|
|
2329
|
-
/**
|
|
3507
|
+
/**
|
|
3508
|
+
* Mode mask for the natural minor (Aeolian) scale: offsets {0, 2, 3, 5, 7, 8, 10}.
|
|
3509
|
+
*
|
|
3510
|
+
* @category Scales
|
|
3511
|
+
*/
|
|
2330
3512
|
declare const NATURAL_MINOR_MASK = 1453;
|
|
2331
|
-
/**
|
|
3513
|
+
/**
|
|
3514
|
+
* Harmonic minor: natural minor with a raised seventh — offsets {0,2,3,5,7,8,11}.
|
|
3515
|
+
*
|
|
3516
|
+
* @category Scales
|
|
3517
|
+
*/
|
|
2332
3518
|
declare const HARMONIC_MINOR_MASK: number;
|
|
2333
|
-
/**
|
|
3519
|
+
/**
|
|
3520
|
+
* Ascending melodic minor: offsets {0,2,3,5,7,9,11}.
|
|
3521
|
+
*
|
|
3522
|
+
* @category Scales
|
|
3523
|
+
*/
|
|
2334
3524
|
declare const MELODIC_MINOR_MASK: number;
|
|
2335
|
-
/**
|
|
3525
|
+
/**
|
|
3526
|
+
* Dorian mode: offsets {0,2,3,5,7,9,10}.
|
|
3527
|
+
*
|
|
3528
|
+
* @category Scales
|
|
3529
|
+
*/
|
|
2336
3530
|
declare const DORIAN_MASK: number;
|
|
2337
|
-
/**
|
|
3531
|
+
/**
|
|
3532
|
+
* Phrygian mode: offsets {0,1,3,5,7,8,10}.
|
|
3533
|
+
*
|
|
3534
|
+
* @category Scales
|
|
3535
|
+
*/
|
|
2338
3536
|
declare const PHRYGIAN_MASK: number;
|
|
2339
|
-
/**
|
|
3537
|
+
/**
|
|
3538
|
+
* Lydian mode: offsets {0,2,4,6,7,9,11}.
|
|
3539
|
+
*
|
|
3540
|
+
* @category Scales
|
|
3541
|
+
*/
|
|
2340
3542
|
declare const LYDIAN_MASK: number;
|
|
2341
|
-
/**
|
|
3543
|
+
/**
|
|
3544
|
+
* Mixolydian mode: offsets {0,2,4,5,7,9,10}.
|
|
3545
|
+
*
|
|
3546
|
+
* @category Scales
|
|
3547
|
+
*/
|
|
2342
3548
|
declare const MIXOLYDIAN_MASK: number;
|
|
2343
|
-
/**
|
|
3549
|
+
/**
|
|
3550
|
+
* Locrian mode: offsets {0,1,3,5,6,8,10}.
|
|
3551
|
+
*
|
|
3552
|
+
* @category Scales
|
|
3553
|
+
*/
|
|
2344
3554
|
declare const LOCRIAN_MASK: number;
|
|
2345
|
-
/**
|
|
3555
|
+
/**
|
|
3556
|
+
* Major pentatonic: offsets {0,2,4,7,9}.
|
|
3557
|
+
*
|
|
3558
|
+
* @category Scales
|
|
3559
|
+
*/
|
|
2346
3560
|
declare const MAJOR_PENTATONIC_MASK: number;
|
|
2347
|
-
/**
|
|
3561
|
+
/**
|
|
3562
|
+
* Minor pentatonic: offsets {0,3,5,7,10}.
|
|
3563
|
+
*
|
|
3564
|
+
* @category Scales
|
|
3565
|
+
*/
|
|
2348
3566
|
declare const MINOR_PENTATONIC_MASK: number;
|
|
2349
|
-
/**
|
|
3567
|
+
/**
|
|
3568
|
+
* Hexatonic blues scale: minor pentatonic plus the flat-fifth — {0,3,5,6,7,10}.
|
|
3569
|
+
*
|
|
3570
|
+
* @category Scales
|
|
3571
|
+
*/
|
|
2350
3572
|
declare const BLUES_MASK: number;
|
|
2351
|
-
/**
|
|
3573
|
+
/**
|
|
3574
|
+
* Whole-tone scale: offsets {0,2,4,6,8,10}.
|
|
3575
|
+
*
|
|
3576
|
+
* @category Scales
|
|
3577
|
+
*/
|
|
2352
3578
|
declare const WHOLE_TONE_MASK: number;
|
|
2353
|
-
/**
|
|
3579
|
+
/**
|
|
3580
|
+
* Octatonic (half-step first): offsets {0,1,3,4,6,7,9,10}.
|
|
3581
|
+
*
|
|
3582
|
+
* @category Scales
|
|
3583
|
+
*/
|
|
2354
3584
|
declare const OCTATONIC_HALF_WHOLE_MASK: number;
|
|
2355
|
-
/**
|
|
3585
|
+
/**
|
|
3586
|
+
* Octatonic (whole-step first): offsets {0,2,3,5,6,8,9,11}.
|
|
3587
|
+
*
|
|
3588
|
+
* @category Scales
|
|
3589
|
+
*/
|
|
2356
3590
|
declare const OCTATONIC_WHOLE_HALF_MASK: number;
|
|
2357
|
-
/**
|
|
3591
|
+
/**
|
|
3592
|
+
* Chromatic scale: all twelve pitch classes.
|
|
3593
|
+
*
|
|
3594
|
+
* @category Scales
|
|
3595
|
+
*/
|
|
2358
3596
|
declare const CHROMATIC_MASK = 4095;
|
|
2359
|
-
/**
|
|
3597
|
+
/**
|
|
3598
|
+
* Named scale masks addressable by {@link scaleByName}.
|
|
3599
|
+
*
|
|
3600
|
+
* @category Scales
|
|
3601
|
+
*/
|
|
2360
3602
|
declare const NAMED_SCALES: Record<string, number>;
|
|
2361
|
-
/**
|
|
3603
|
+
/**
|
|
3604
|
+
* Build a `KeyScale` for a major key on the given root pitch class.
|
|
3605
|
+
*
|
|
3606
|
+
* @example
|
|
3607
|
+
* ```ts
|
|
3608
|
+
* import { majorKey } from '@libraz/libcantus';
|
|
3609
|
+
* const cMajor = majorKey(0); // C major: { rootPc: 0, modeMask12: MAJOR_MASK }
|
|
3610
|
+
* ```
|
|
3611
|
+
*
|
|
3612
|
+
* @category Scales
|
|
3613
|
+
*/
|
|
2362
3614
|
declare function majorKey(rootPc: number): KeyScale;
|
|
2363
|
-
/**
|
|
3615
|
+
/**
|
|
3616
|
+
* Build a `KeyScale` for a natural-minor key on the given root pitch class.
|
|
3617
|
+
*
|
|
3618
|
+
* @example
|
|
3619
|
+
* ```ts
|
|
3620
|
+
* import { minorKey } from '@libraz/libcantus';
|
|
3621
|
+
* const aMinor = minorKey(9); // A natural minor: { rootPc: 9, modeMask12: NATURAL_MINOR_MASK }
|
|
3622
|
+
* ```
|
|
3623
|
+
*
|
|
3624
|
+
* @category Scales
|
|
3625
|
+
*/
|
|
2364
3626
|
declare function minorKey(rootPc: number): KeyScale;
|
|
2365
3627
|
/**
|
|
2366
3628
|
* Build a `KeyScale` from a named scale (see {@link NAMED_SCALES}).
|
|
@@ -2369,6 +3631,14 @@ declare function minorKey(rootPc: number): KeyScale;
|
|
|
2369
3631
|
* @param rootPc The root pitch class.
|
|
2370
3632
|
* @returns The key/scale.
|
|
2371
3633
|
* @throws If the name is not a known scale.
|
|
3634
|
+
*
|
|
3635
|
+
* @example
|
|
3636
|
+
* ```ts
|
|
3637
|
+
* import { scaleByName } from '@libraz/libcantus';
|
|
3638
|
+
* const dDorian = scaleByName('dorian', 2); // D Dorian, rootPc 2
|
|
3639
|
+
* ```
|
|
3640
|
+
*
|
|
3641
|
+
* @category Scales
|
|
2372
3642
|
*/
|
|
2373
3643
|
declare function scaleByName(name: string, rootPc: number): KeyScale;
|
|
2374
3644
|
/**
|
|
@@ -2377,6 +3647,16 @@ declare function scaleByName(name: string, rootPc: number): KeyScale;
|
|
|
2377
3647
|
* @param pitch MIDI pitch or bare pitch class.
|
|
2378
3648
|
* @param key The key/scale to test against.
|
|
2379
3649
|
* @returns True if the pitch class is a member of the scale.
|
|
3650
|
+
*
|
|
3651
|
+
* @example
|
|
3652
|
+
* ```ts
|
|
3653
|
+
* import { majorKey, isScaleTone } from '@libraz/libcantus';
|
|
3654
|
+
* const key = majorKey(0); // C major
|
|
3655
|
+
* isScaleTone(64, key); // true — E is in C major
|
|
3656
|
+
* isScaleTone(61, key); // false — C# is not
|
|
3657
|
+
* ```
|
|
3658
|
+
*
|
|
3659
|
+
* @category Scales
|
|
2380
3660
|
*/
|
|
2381
3661
|
declare function isScaleTone(pitch: number, key: KeyScale): boolean;
|
|
2382
3662
|
/**
|
|
@@ -2388,6 +3668,8 @@ declare function isScaleTone(pitch: number, key: KeyScale): boolean;
|
|
|
2388
3668
|
* @param pitch MIDI pitch to snap.
|
|
2389
3669
|
* @param key The key/scale to snap to.
|
|
2390
3670
|
* @returns The nearest in-scale MIDI pitch.
|
|
3671
|
+
*
|
|
3672
|
+
* @category Scales
|
|
2391
3673
|
*/
|
|
2392
3674
|
declare function nearestScaleTone(pitch: number, key: KeyScale): number;
|
|
2393
3675
|
/**
|
|
@@ -2396,6 +3678,8 @@ declare function nearestScaleTone(pitch: number, key: KeyScale): number;
|
|
|
2396
3678
|
* @param pitch MIDI pitch or bare pitch class.
|
|
2397
3679
|
* @param key The key/scale to measure against.
|
|
2398
3680
|
* @returns The 0-based degree, or -1 if the pitch is not in the scale.
|
|
3681
|
+
*
|
|
3682
|
+
* @category Scales
|
|
2399
3683
|
*/
|
|
2400
3684
|
declare function pitchToScaleDegree(pitch: number, key: KeyScale): number;
|
|
2401
3685
|
/**
|
|
@@ -2403,6 +3687,8 @@ declare function pitchToScaleDegree(pitch: number, key: KeyScale): number;
|
|
|
2403
3687
|
*
|
|
2404
3688
|
* @param key The key/scale to enumerate.
|
|
2405
3689
|
* @returns The member pitch classes, sorted ascending in [0, 11].
|
|
3690
|
+
*
|
|
3691
|
+
* @category Scales
|
|
2406
3692
|
*/
|
|
2407
3693
|
declare function diatonicPitchClasses(key: KeyScale): number[];
|
|
2408
3694
|
/**
|
|
@@ -2413,6 +3699,8 @@ declare function diatonicPitchClasses(key: KeyScale): number[];
|
|
|
2413
3699
|
*
|
|
2414
3700
|
* @param key The key/scale to enumerate.
|
|
2415
3701
|
* @returns The member pitch classes ordered by scale degree.
|
|
3702
|
+
*
|
|
3703
|
+
* @category Scales
|
|
2416
3704
|
*/
|
|
2417
3705
|
declare function scaleTonesInDegreeOrder(key: KeyScale): number[];
|
|
2418
3706
|
|
|
@@ -2437,6 +3725,7 @@ declare function scaleTonesInDegreeOrder(key: KeyScale): number[];
|
|
|
2437
3725
|
* @param tonic The spelled tonic (its letter anchors the spelling).
|
|
2438
3726
|
* @param key The key/scale.
|
|
2439
3727
|
* @returns The spelled note (without octave).
|
|
3728
|
+
* @category Pitch & Intervals
|
|
2440
3729
|
*/
|
|
2441
3730
|
declare function spellPitchClass(pc: number, tonic: Note$1, key: KeyScale): Note$1;
|
|
2442
3731
|
/**
|
|
@@ -2448,6 +3737,13 @@ declare function spellPitchClass(pc: number, tonic: Note$1, key: KeyScale): Note
|
|
|
2448
3737
|
* @param tonic The spelled tonic.
|
|
2449
3738
|
* @param key The key/scale.
|
|
2450
3739
|
* @returns Spelled notes, one per scale degree.
|
|
3740
|
+
* @example
|
|
3741
|
+
* ```ts
|
|
3742
|
+
* import { spellScale, noteNames, parseNote, majorKey } from '@libraz/libcantus';
|
|
3743
|
+
* noteNames(spellScale(parseNote('C'), majorKey(0)));
|
|
3744
|
+
* // ['C', 'D', 'E', 'F', 'G', 'A', 'B']
|
|
3745
|
+
* ```
|
|
3746
|
+
* @category Pitch & Intervals
|
|
2451
3747
|
*/
|
|
2452
3748
|
declare function spellScale(tonic: Note$1, key: KeyScale): Note$1[];
|
|
2453
3749
|
/**
|
|
@@ -2457,6 +3753,7 @@ declare function spellScale(tonic: Note$1, key: KeyScale): Note$1[];
|
|
|
2457
3753
|
* @param tonic The spelled tonic.
|
|
2458
3754
|
* @param key The key/scale.
|
|
2459
3755
|
* @returns Spelled notes, in input order.
|
|
3756
|
+
* @category Pitch & Intervals
|
|
2460
3757
|
*/
|
|
2461
3758
|
declare function spellPitchClasses(pcs: number[], tonic: Note$1, key: KeyScale): Note$1[];
|
|
2462
3759
|
/**
|
|
@@ -2471,6 +3768,14 @@ declare function spellPitchClasses(pcs: number[], tonic: Note$1, key: KeyScale):
|
|
|
2471
3768
|
* @param tonic The spelled tonic of the key.
|
|
2472
3769
|
* @param key The key/scale.
|
|
2473
3770
|
* @returns Spelled chord tones, root first.
|
|
3771
|
+
* @example
|
|
3772
|
+
* ```ts
|
|
3773
|
+
* import { spellChord, noteNames, parseNote, majorKey, makeChord } from '@libraz/libcantus';
|
|
3774
|
+
* // G7 in C major
|
|
3775
|
+
* noteNames(spellChord(makeChord(7, 'dom7'), parseNote('C'), majorKey(0)));
|
|
3776
|
+
* // ['G', 'B', 'D', 'F']
|
|
3777
|
+
* ```
|
|
3778
|
+
* @category Pitch & Intervals
|
|
2474
3779
|
*/
|
|
2475
3780
|
declare function spellChord(chord: Chord$1, tonic: Note$1, key: KeyScale): Note$1[];
|
|
2476
3781
|
/**
|
|
@@ -2478,6 +3783,7 @@ declare function spellChord(chord: Chord$1, tonic: Note$1, key: KeyScale): Note$
|
|
|
2478
3783
|
*
|
|
2479
3784
|
* @param notes The notes.
|
|
2480
3785
|
* @returns Their formatted names.
|
|
3786
|
+
* @category Pitch & Intervals
|
|
2481
3787
|
*/
|
|
2482
3788
|
declare function noteNames(notes: Note$1[]): string[];
|
|
2483
3789
|
|
|
@@ -2493,13 +3799,26 @@ declare function noteNames(notes: Note$1[]): string[];
|
|
|
2493
3799
|
* Parse a lead-sheet chord symbol into a {@link Chord}.
|
|
2494
3800
|
*
|
|
2495
3801
|
* Accepts `<root><quality>[/<bass>]`, e.g. `Cmaj7`, `F#m7b5`, `Bb7`, `C/G`,
|
|
2496
|
-
* `C6/9`.
|
|
2497
|
-
*
|
|
2498
|
-
*
|
|
3802
|
+
* `C6/9`, `C6/9/E`. Root and bass letters are case-insensitive, matching
|
|
3803
|
+
* {@link parseNote}. A `/` is ambiguous between a slash bass and the `6/9`
|
|
3804
|
+
* quality; the text splits at the last `/` whose right-hand side is a valid
|
|
3805
|
+
* bare note preceded by a recognized quality, so `6/9` (with or without a
|
|
3806
|
+
* further slash bass) parses as a quality.
|
|
3807
|
+
*
|
|
3808
|
+
* The parsed chord carries the root/bass spellings as enharmonic hints
|
|
3809
|
+
* (`rootSpelling`/`bassSpelling`) so {@link formatChordSymbol} can reproduce
|
|
3810
|
+
* flat spellings such as `Bbmaj7` instead of respelling them with sharps.
|
|
2499
3811
|
*
|
|
2500
3812
|
* @param text The chord symbol text.
|
|
2501
3813
|
* @returns The parsed chord.
|
|
2502
3814
|
* @throws If the root or quality is not recognized.
|
|
3815
|
+
* @example
|
|
3816
|
+
* ```ts
|
|
3817
|
+
* import { parseChordSymbol, formatChordSymbol } from '@libraz/libcantus';
|
|
3818
|
+
* const c = parseChordSymbol('Bbmaj7');
|
|
3819
|
+
* formatChordSymbol(c); // 'Bbmaj7' — flat spelling is preserved on round-trip
|
|
3820
|
+
* ```
|
|
3821
|
+
* @category Chords
|
|
2503
3822
|
*/
|
|
2504
3823
|
declare function parseChordSymbol(text: string): Chord$1;
|
|
2505
3824
|
/**
|
|
@@ -2507,12 +3826,21 @@ declare function parseChordSymbol(text: string): Chord$1;
|
|
|
2507
3826
|
*
|
|
2508
3827
|
* The inverse of {@link parseChordSymbol}: each quality maps to one canonical
|
|
2509
3828
|
* suffix spelling, and a slash bass is appended only when it differs from the
|
|
2510
|
-
* root.
|
|
3829
|
+
* root. When the chord carries `rootSpelling`/`bassSpelling` hints (as chords
|
|
3830
|
+
* from {@link parseChordSymbol} do) and no explicit `flats` preference is
|
|
3831
|
+
* given, the hints are reused so flat symbols round-trip unchanged.
|
|
2511
3832
|
*
|
|
2512
3833
|
* @param chord The chord to format.
|
|
2513
3834
|
* @param opts Formatting options.
|
|
2514
|
-
* @param opts.flats Prefer flat spellings over sharps for altered roots/basses
|
|
3835
|
+
* @param opts.flats Prefer flat spellings over sharps for altered roots/basses;
|
|
3836
|
+
* passing either `true` or `false` overrides any spelling hints.
|
|
2515
3837
|
* @returns The chord symbol text.
|
|
3838
|
+
* @example
|
|
3839
|
+
* ```ts
|
|
3840
|
+
* import { makeChord, formatChordSymbol } from '@libraz/libcantus';
|
|
3841
|
+
* formatChordSymbol(makeChord(0, 'min7')); // 'Cm7'
|
|
3842
|
+
* ```
|
|
3843
|
+
* @category Chords
|
|
2516
3844
|
*/
|
|
2517
3845
|
declare function formatChordSymbol(chord: Chord$1, opts?: {
|
|
2518
3846
|
flats?: boolean;
|
|
@@ -2525,6 +3853,12 @@ declare function formatChordSymbol(chord: Chord$1, opts?: {
|
|
|
2525
3853
|
* @param opts Formatting options for the result; see {@link formatChordSymbol}.
|
|
2526
3854
|
* @returns The transposed chord symbol text.
|
|
2527
3855
|
* @throws If `text` does not parse as a chord symbol.
|
|
3856
|
+
* @example
|
|
3857
|
+
* ```ts
|
|
3858
|
+
* import { transposeChordSymbol } from '@libraz/libcantus';
|
|
3859
|
+
* transposeChordSymbol('C/G', 2); // 'D/A'
|
|
3860
|
+
* ```
|
|
3861
|
+
* @category Chords
|
|
2528
3862
|
*/
|
|
2529
3863
|
declare function transposeChordSymbol(text: string, semitones: number, opts?: {
|
|
2530
3864
|
flats?: boolean;
|
|
@@ -2542,6 +3876,8 @@ declare function transposeChordSymbol(text: string, semitones: number, opts?: {
|
|
|
2542
3876
|
/**
|
|
2543
3877
|
* An equal-tempered tuning: a reference pitch and a number of equal divisions of
|
|
2544
3878
|
* the octave. With `divisions === 12` a step index is an ordinary MIDI number.
|
|
3879
|
+
*
|
|
3880
|
+
* @category Pitch & Intervals
|
|
2545
3881
|
*/
|
|
2546
3882
|
type Tuning = {
|
|
2547
3883
|
/** Step index (MIDI number when `divisions` is 12) whose frequency is `refFreq`. */
|
|
@@ -2551,7 +3887,11 @@ type Tuning = {
|
|
|
2551
3887
|
/** Equal divisions of the octave (12 standard; e.g. 19 or 31 for microtonal). */
|
|
2552
3888
|
divisions: number;
|
|
2553
3889
|
};
|
|
2554
|
-
/**
|
|
3890
|
+
/**
|
|
3891
|
+
* Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.
|
|
3892
|
+
*
|
|
3893
|
+
* @category Pitch & Intervals
|
|
3894
|
+
*/
|
|
2555
3895
|
declare const TWELVE_TET: Tuning;
|
|
2556
3896
|
/**
|
|
2557
3897
|
* Build an equal temperament with `n` divisions of the octave.
|
|
@@ -2560,6 +3900,13 @@ declare const TWELVE_TET: Tuning;
|
|
|
2560
3900
|
* @param refFreq Reference frequency in Hz (default 440).
|
|
2561
3901
|
* @param refStep Step index of the reference (default 69).
|
|
2562
3902
|
* @returns The tuning.
|
|
3903
|
+
* @example
|
|
3904
|
+
* ```ts
|
|
3905
|
+
* import { edo, frequencyOf } from '@libraz/libcantus';
|
|
3906
|
+
* const et19 = edo(19); // { refStep: 69, refFreq: 440, divisions: 19 }
|
|
3907
|
+
* frequencyOf(70, et19); // one 19-EDO step above A4
|
|
3908
|
+
* ```
|
|
3909
|
+
* @category Pitch & Intervals
|
|
2563
3910
|
*/
|
|
2564
3911
|
declare function edo(n: number, refFreq?: number, refStep?: number): Tuning;
|
|
2565
3912
|
/**
|
|
@@ -2568,6 +3915,13 @@ declare function edo(n: number, refFreq?: number, refStep?: number): Tuning;
|
|
|
2568
3915
|
* @param step Step index (a MIDI number under 12-EDO).
|
|
2569
3916
|
* @param tuning The tuning (default 12-TET).
|
|
2570
3917
|
* @returns The frequency in Hz.
|
|
3918
|
+
* @example
|
|
3919
|
+
* ```ts
|
|
3920
|
+
* import { frequencyOf } from '@libraz/libcantus';
|
|
3921
|
+
* frequencyOf(69); // 440 (A4 in 12-TET)
|
|
3922
|
+
* frequencyOf(60); // middle C in Hz
|
|
3923
|
+
* ```
|
|
3924
|
+
* @category Pitch & Intervals
|
|
2571
3925
|
*/
|
|
2572
3926
|
declare function frequencyOf(step: number, tuning?: Tuning): number;
|
|
2573
3927
|
/**
|
|
@@ -2577,6 +3931,7 @@ declare function frequencyOf(step: number, tuning?: Tuning): number;
|
|
|
2577
3931
|
* @param freq Frequency in Hz.
|
|
2578
3932
|
* @param tuning The tuning (default 12-TET).
|
|
2579
3933
|
* @returns The nearest step index.
|
|
3934
|
+
* @category Pitch & Intervals
|
|
2580
3935
|
*/
|
|
2581
3936
|
declare function nearestStep(freq: number, tuning?: Tuning): number;
|
|
2582
3937
|
/**
|
|
@@ -2585,6 +3940,7 @@ declare function nearestStep(freq: number, tuning?: Tuning): number;
|
|
|
2585
3940
|
* @param a Lower/first frequency in Hz.
|
|
2586
3941
|
* @param b Upper/second frequency in Hz.
|
|
2587
3942
|
* @returns Cents from `a` to `b` (negative if `b` is lower).
|
|
3943
|
+
* @category Pitch & Intervals
|
|
2588
3944
|
*/
|
|
2589
3945
|
declare function centsBetweenFreq(a: number, b: number): number;
|
|
2590
3946
|
/**
|
|
@@ -2593,6 +3949,7 @@ declare function centsBetweenFreq(a: number, b: number): number;
|
|
|
2593
3949
|
* @param steps Number of steps.
|
|
2594
3950
|
* @param tuning The tuning (default 12-TET).
|
|
2595
3951
|
* @returns The cents.
|
|
3952
|
+
* @category Pitch & Intervals
|
|
2596
3953
|
*/
|
|
2597
3954
|
declare function centsOfSteps(steps: number, tuning?: Tuning): number;
|
|
2598
3955
|
/**
|
|
@@ -2602,11 +3959,14 @@ declare function centsOfSteps(steps: number, tuning?: Tuning): number;
|
|
|
2602
3959
|
* @param numerator Ratio numerator.
|
|
2603
3960
|
* @param denominator Ratio denominator.
|
|
2604
3961
|
* @returns The interval in cents.
|
|
3962
|
+
* @category Pitch & Intervals
|
|
2605
3963
|
*/
|
|
2606
3964
|
declare function ratioToCents(numerator: number, denominator: number): number;
|
|
2607
3965
|
/**
|
|
2608
3966
|
* Five-limit just-intonation ratios for the twelve interval classes above a
|
|
2609
3967
|
* unison, indexed by semitone class (0..12).
|
|
3968
|
+
*
|
|
3969
|
+
* @category Pitch & Intervals
|
|
2610
3970
|
*/
|
|
2611
3971
|
declare const JUST_RATIOS: Record<number, [number, number]>;
|
|
2612
3972
|
/**
|
|
@@ -2615,7 +3975,8 @@ declare const JUST_RATIOS: Record<number, [number, number]>;
|
|
|
2615
3975
|
*
|
|
2616
3976
|
* @param semitoneClass Semitone class in [0, 12].
|
|
2617
3977
|
* @returns The deviation in cents, or NaN if the class has no listed ratio.
|
|
3978
|
+
* @category Pitch & Intervals
|
|
2618
3979
|
*/
|
|
2619
3980
|
declare function justDeviationCents(semitoneClass: number): number;
|
|
2620
3981
|
|
|
2621
|
-
export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, BLUES_MASK, type BarPosition, type BassLineOptions, type BassSegment, type BassStyle, type BorrowedChord, type BorrowedSource, CHROMATIC_MASK, type Cadence, type CadenceHit, Chord, type ChordAnalysis, type Chord$1 as ChordData, type ChordMatch, type ChordQuality, type ChordScaleMatch, type ChordScaleReportEntry, type ChordSegment, type ChordTimeline, type ChordTimelineOptions, type ChordTimelineResult, type Conflict, type CounterMelodyOptions, DORIAN_MASK, type DrumGenOptions, type DrumHit, type DrumRole, type GenerateMotifOptions, type GenerateProgressionOptions, type GeneratedChord, type GrooveFeel, type GrooveSlot, type GrooveStyle, type GrooveTemplate, HARMONIC_MINOR_MASK, type HarmonicFunction, type HarmonizeOptions, type HarmonizeResult, type HarmonyRole, type HumanizeOptions, Interval, IntervalQuality, JUST_RATIOS, Key, type KeyMatch, type KeyScale, LOCRIAN_MASK, LYDIAN_MASK, type LockLevel, MAJOR_MASK, MAJOR_PENTATONIC_MASK, MELODIC_MINOR_MASK, MINOR_PENTATONIC_MASK, MIXOLYDIAN_MASK, type MelodyNote, type MotifCell, type MotifContour, type MotifNote, type MotifTransform, NAMED_SCALES, NATURAL_MINOR_MASK, Note, type Note$1 as NoteData, type NoteEvent, NoteSafety, OCTATONIC_HALF_WHOLE_MASK, OCTATONIC_WHOLE_HALF_MASK, PHRYGIAN_MASK, type ProgFunction, type ProgStyle, Progression, type ProgressionPreset, ReasonFlag, type RhythmEvent, type RhythmOptions, type Rng, SATB_RANGES, type SafetyProfile, type SafetyQuery, type SafetyResult, type ScaleChoice, type Section, type SpelledInterval, type StyledVoicingOptions, type SubstituteOptions, type Substitution, type SubstitutionType, TWELVE_TET, type TensionPoint, type TheoryLabel, type TimeSignature, type TrackAnalysis, type TrackRole, type Tuning, type VoiceNote, type VoiceRange, type VoiceSnapshot, type VoicedRole, type VoicingOptions, type VoicingStyle, WHOLE_TONE_MASK, analyzeArrangement, analyzeChord, analyzeVoice, applyGrooveTemplate, availableTensions, avoidNotes, barPositionToBeat, beatToBarPosition, beatsPerBar, borrowedSource, centsBetweenFreq, centsOfSteps, chordFromDegree, chordPitchClasses, chordQualities, chordScaleReport, chordScales, chordTimelineFromChords, chordTimelineFromNotes, chordToRoman, chordToneRole, classifyInterval, createRng, createsHiddenParallelPerfect, createsParallelOctave, createsParallelPerfect, createsParallelUnison, createsVerticalDissonance, createsVoiceCrossing, createsVoiceOverlap, detectCadence, detectCadences, detectChord, detectChordBest, detectKey, developMotif, diatonicPitchClasses, diatonicSeventh, diatonicTriad, edo, enumerateSafePitches, evaluateSafety, exceedsSpacing, extractGrooveTemplate, formatChordSymbol, formatNote, formatTimeSignature, frequencyOf, functionOf, generateBassLine, generateCounterMelody, generateDrums, generateMotif, generateProgression, generateRhythm, harmonizeMelody, humanize, isBorrowedChord, isCompound, isConsonantInterval, isDiatonic, isForbiddenMelodicLeap, isLeadingToneResolution, isMinorKey, isPerfectInterval, isScaleTone, isStrongBeat, justDeviationCents, majorKey, makeChord, maskFromOffsets, metricWeight, midiToNote, minorKey, modalInterchangePalette, nearestScaleTone, nearestStep, negativeHarmonyMirror, nextVoicing, noteNames, noteToMidi, noteToPitchClass, onsetWeightCurve, parallelKey, parseChordSymbol, parseNote, parseTimeSignature, pitchToScaleDegree, progressions, progressionsByStyle, pulsesPerBar, ratioToCents, rhythmDensity, roleOf, romanToChord, scaleByName, scaleMatchesChord, scaleTonesInDegreeOrder, scalesForChanges, secondaryDominant, spellChord, spellPitchClass, spellPitchClasses, spellScale, spelledInterval, substituteChord, tensionCurve, transformMotif, transposeChordSymbol, tuplet, voiceChord, voiceChordStyled, voiceLeadingCost, voiceProgression };
|
|
3982
|
+
export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, BLUES_MASK, type BarPosition, type BassLineOptions, type BassSegment, type BassStyle, type BorrowedChord, type BorrowedSource, CHROMATIC_MASK, type Cadence, type CadenceHit, Chord, type ChordAnalysis, type Chord$1 as ChordData, type ChordMatch, type ChordQuality, type ChordScaleMatch, type ChordScaleReportEntry, type ChordSegment, type ChordTimeline, type ChordTimelineOptions, type ChordTimelineResult, type Conflict, type CounterMelodyOptions, DORIAN_MASK, type DrumGenOptions, type DrumHit, type DrumRole, type EuclideanKick, type GenerateMotifOptions, type GenerateProgressionOptions, type GeneratedChord, type GrooveFeel, type GrooveSlot, type GrooveStyle, type GrooveTemplate, HARMONIC_MINOR_MASK, type HarmonicFunction, type HarmonizeOptions, type HarmonizeResult, type HarmonyRole, type HumanizeOptions, Interval, IntervalQuality, JUST_RATIOS, Key, type KeyMatch, type KeyScale, LOCRIAN_MASK, LYDIAN_MASK, type LockLevel, MAJOR_MASK, MAJOR_PENTATONIC_MASK, MELODIC_MINOR_MASK, MINOR_PENTATONIC_MASK, MIXOLYDIAN_MASK, type MelodyNote, type MotifCell, type MotifContour, type MotifNote, type MotifTransform, NAMED_SCALES, NATURAL_MINOR_MASK, Note, type Note$1 as NoteData, type NoteEvent, NoteSafety, OCTATONIC_HALF_WHOLE_MASK, OCTATONIC_WHOLE_HALF_MASK, PHRYGIAN_MASK, type ProgFunction, type ProgStyle, Progression, type ProgressionPreset, ReasonFlag, type RhythmEvent, type RhythmOptions, type Rng, SATB_RANGES, type SafetyProfile, type SafetyQuery, type SafetyResult, type ScaleChoice, type Section, type SpelledInterval, type StyledVoicingOptions, type SubstituteOptions, type Substitution, type SubstitutionType, TWELVE_TET, type TensionPoint, type TheoryLabel, type TimeSignature, type TrackAnalysis, type TrackRole, type Tuning, type VoiceNote, type VoiceRange, type VoiceSnapshot, type VoicedRole, type VoicingOptions, type VoicingStyle, WHOLE_TONE_MASK, analyzeArrangement, analyzeChord, analyzeVoice, applyGrooveTemplate, availableTensions, avoidNotes, barPositionToBeat, beatToBarPosition, beatsPerBar, borrowedSource, centsBetweenFreq, centsOfSteps, chordFromDegree, chordPitchClasses, chordQualities, chordScaleReport, chordScales, chordTimelineFromChords, chordTimelineFromNotes, chordToRoman, chordToneRole, classifyInterval, createRng, createsHiddenParallelPerfect, createsParallelOctave, createsParallelPerfect, createsParallelUnison, createsVerticalDissonance, createsVoiceCrossing, createsVoiceOverlap, detectCadence, detectCadences, detectChord, detectChordBest, detectKey, developMotif, diatonicPitchClasses, diatonicSeventh, diatonicTriad, edo, enumerateSafePitches, evaluateSafety, exceedsSpacing, extractGrooveTemplate, formatChordSymbol, formatNote, formatTimeSignature, frequencyOf, functionOf, generateBassLine, generateCounterMelody, generateDrums, generateMotif, generateProgression, generateRhythm, harmonizeMelody, humanize, isBorrowedChord, isCompound, isConsonantInterval, isDiatonic, isForbiddenMelodicLeap, isLeadingToneResolution, isMinorKey, isPerfectInterval, isScaleTone, isStrongBeat, justDeviationCents, majorKey, makeChord, maskFromOffsets, metricWeight, midiToNote, minorKey, modalInterchangePalette, nearestScaleTone, nearestStep, negativeHarmonyMirror, nextVoicing, noteNames, noteToMidi, noteToPitchClass, onsetWeightCurve, parallelKey, parseChordSymbol, parseNote, parseTimeSignature, pitchToScaleDegree, progressions, progressionsByStyle, pulsesPerBar, ratioToCents, rhythmDensity, roleOf, romanToChord, scaleByName, scaleMatchesChord, scaleTonesInDegreeOrder, scalesForChanges, secondaryDominant, spellChord, spellPitchClass, spellPitchClasses, spellScale, spelledInterval, substituteChord, tensionCurve, transformMotif, transposeChordSymbol, tuplet, voiceChord, voiceChordStyled, voiceLeadingCost, voiceProgression };
|