@libraz/libcantus 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -261
- package/README_ja.md +115 -0
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +213 -456
- package/dist/analyze/index.d.ts +213 -456
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/chunk-ELCTZUJJ.js +379 -0
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +103 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +96 -345
- package/dist/core/index.d.ts +96 -345
- package/dist/core/index.js +104 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +437 -1095
- package/dist/generate/index.d.ts +437 -1095
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +358 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +407 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4685 -243
- package/dist/model/index.d.ts +4685 -243
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +513 -173
- package/dist/theory/index.d.ts +513 -173
- package/dist/theory/index.js +118 -24
- package/package.json +16 -11
- package/dist/chunk-36N73KZ3.cjs +0 -246
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs +0 -3077
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs +0 -498
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js +0 -3077
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs +0 -679
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js +0 -479
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js +0 -1449
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs +0 -30
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs +0 -479
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js +0 -173
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-HKGYCQ34.js +0 -53
- package/dist/chunk-HKGYCQ34.js.map +0 -1
- package/dist/chunk-I24MV65U.js +0 -106
- package/dist/chunk-I24MV65U.js.map +0 -1
- package/dist/chunk-KMHOICIY.js +0 -679
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs +0 -1117
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NKIBQ6PT.js +0 -30
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs +0 -106
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs +0 -1449
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js +0 -327
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js +0 -845
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs +0 -327
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs +0 -845
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js +0 -1117
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-RUMU3UA6.cjs +0 -53
- package/dist/chunk-RUMU3UA6.cjs.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-XEBG3G53.js +0 -498
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js +0 -246
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs +0 -173
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- package/dist/function-BtiZb9i9.d.ts +0 -208
- package/dist/function-DNugAOrt.d.cts +0 -208
- package/dist/index-6VR8-kit.d.ts +0 -143
- package/dist/index-70p168VM.d.ts +0 -192
- package/dist/index-Be74mcon.d.cts +0 -152
- package/dist/index-BtXT6UQ0.d.cts +0 -192
- package/dist/index-C5UBbQ4G.d.ts +0 -152
- package/dist/index-C83mZI1P.d.ts +0 -242
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-OG65ocOr.d.cts +0 -272
- package/dist/index-OG65ocOr.d.ts +0 -272
- package/dist/index-tJhkmQgE.d.cts +0 -143
- package/dist/index-y_ak9_Hi.d.cts +0 -242
- package/dist/styled-DIRkk9Uz.d.ts +0 -506
- package/dist/styled-DJbkJQfj.d.cts +0 -506
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
|
@@ -1,1294 +1,636 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
export { d as ChordToneRole,
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
import { a as Articulation, g as NoteEvent, I as IntervalLike } from '../index-B9MNZboQ.cjs';
|
|
2
|
+
export { b as IntervalQualityLabel, d as KeyScale, S as SpelledInterval } from '../index-B9MNZboQ.cjs';
|
|
3
|
+
import { e as ChordSegment, c as ChordLike, K as KeyLike } from '../index-C_8o--VE.cjs';
|
|
4
|
+
export { b as ChordData, d as ChordQuality, g as ChordSpan, j as ChordToneRole, l as PitchSpelling, m as ProfileWeights } from '../index-C_8o--VE.cjs';
|
|
5
|
+
export { B as BorrowedSource, e as ChordTimeline, H as HarmonicFunction } from '../index-Dzr13q3d.cjs';
|
|
6
|
+
import { f as StringedProfile, P as PercussionProfile, c as Limb } from '../positional-DvH_sd8u.cjs';
|
|
7
|
+
export { I as InstrumentProfile, a as InstrumentProfileCommon, d as PositionalRng, S as SeedPath } from '../positional-DvH_sd8u.cjs';
|
|
8
|
+
import { T as TimeSignature } from '../index-BkGmlyI3.cjs';
|
|
9
|
+
export { H as HarmonyRole } from '../index-B4o55NDr.cjs';
|
|
10
|
+
import { a3 as Vocabulary, s as GridEvent, r as Genre, q as GenerationContextInput, m as DrumStyle, $ as Section, n as DrumVoice, t as GrooveFeel, k as DrumHit } from '../index-BGFW-8P2.cjs';
|
|
11
|
+
export { B as BAR_STEPS, a as BASS_STYLES, b as BEAT_STEPS, c as BORROWED_DEGREES, d as BassLineOptions, e as BassSegment, f as BassStyle, g as BorrowedChord, C as Complexity, h as CounterMelodyOptions, D as DRUM_NOTES, i as DeformOptions, j as Draw, l as DrumRole, o as DrumsOptions, E as EuclideanKick, G as GENRES, p as GenerationContext, u as GrooveSlot, v as GrooveStyle, w as GrooveTemplate, H as HarmonizeOptions, x as HarmonizePlacement, y as HarmonizeResult, z as HumanizeOptions, M as MelodyNote, A as MotifCell, F as MotifContour, I as MotifNote, J as MotifOptions, K as MotifTransform, O as ORNAMENT_STYLES, L as OrnamentOptions, N as OrnamentStyle, P as PROVENANCE_BASES, Q as ProgFunction, R as ProgStyle, S as ProgressionDegree, T as ProgressionOptions, U as ProgressionPreset, V as Provenance, W as ProvenanceBasis, X as ResolvedContext, Y as RhythmEvent, Z as RhythmOptions, _ as STEP_BEATS, a0 as SubstituteOptions, a1 as Substitution, a2 as SubstitutionType, a4 as VocabularyQuery, a5 as applyGrooveTemplate, a6 as assertVocabulary, a7 as deform, a8 as developMotif, a9 as double, aa as doubleTime, ab as drumVoiceOf, ac as extractGrooveTemplate, ad as fitsQuery, ae as generateBassLine, af as generateCounterMelody, ag as generateDrums, ah as generateMotif, ai as generateProgression, aj as generateRhythm, ak as gridMetricWeight, al as halfTime, am as harmonizeMelody, an as humanize, ao as mergeVocabulary, ap as modalInterchangePalette, aq as motifToNoteEvents, ar as negativeHarmonyMirror, as as onsetWeightCurve, at as ornament, au as ornamentBy, av as pickProgressionPreset, aw as pickVocabulary, ax as progressions, ay as progressionsByStyle, az as resolveContext, aA as rhythmDensity, aB as rhythmToNoteEvents, aC as selectVocabulary, aD as substituteChord, aE as syncopate, aF as thin, aG as transformMotif, aH as vocabularyOfKind, aI as withinCeiling } from '../index-BGFW-8P2.cjs';
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
|
-
*
|
|
14
|
+
* The playing-difficulty ceiling, as the generators apply it.
|
|
11
15
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Positions and durations are measured in quarter-note beats, matching the
|
|
20
|
-
* library-wide convention.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A chord sounding over a half-open beat span `[startBeat, endBeat)`.
|
|
25
|
-
*
|
|
26
|
-
* The same record {@link ChordSegment} names, so a timeline segment can be fed
|
|
27
|
-
* straight to the bass generator.
|
|
28
|
-
*
|
|
29
|
-
* @category Composition
|
|
30
|
-
*/
|
|
31
|
-
type BassSegment = ChordSegment;
|
|
32
|
-
/**
|
|
33
|
-
* Every bass-line idiom, in declaration order.
|
|
34
|
-
*
|
|
35
|
-
* @category Composition
|
|
36
|
-
*/
|
|
37
|
-
declare const BASS_STYLES: readonly ["root", "rootFifth", "pop", "walking", "arpeggio"];
|
|
38
|
-
/**
|
|
39
|
-
* The bass-line idiom to generate.
|
|
40
|
-
*
|
|
41
|
-
* @category Composition
|
|
16
|
+
* A ceiling is not an intensity: a passage can be harmonically involved and
|
|
17
|
+
* easy to play, or plain and hard. So difficulty never proposes material — it
|
|
18
|
+
* only rejects what the other complexity dials proposed, and only on the
|
|
19
|
+
* timing layer, where speed is what stands in the way. Whether the note exists
|
|
20
|
+
* on the instrument at all, and whether the hand can hold the shape, belong to
|
|
21
|
+
* the instrument profile and hold regardless of the ceiling.
|
|
42
22
|
*/
|
|
43
|
-
|
|
23
|
+
/** The ceiling's lowest level: what a beginner sustains. */
|
|
24
|
+
declare const MIN_DIFFICULTY = 1;
|
|
25
|
+
/** The ceiling's highest level: what a rudimental player sustains. */
|
|
26
|
+
declare const MAX_DIFFICULTY = 5;
|
|
44
27
|
/**
|
|
45
|
-
*
|
|
28
|
+
* Whether a player at this ceiling keeps up a stream of strokes on one voice.
|
|
46
29
|
*
|
|
47
|
-
* @
|
|
30
|
+
* @param stepBeats Gap between consecutive strokes, in quarter-note beats.
|
|
31
|
+
* @param bpm Tempo, or undefined when the caller gave none.
|
|
32
|
+
* @param difficulty The ceiling, or undefined for no ceiling.
|
|
33
|
+
* @returns True when the stream is inside the ceiling, and whenever either the
|
|
34
|
+
* ceiling or the tempo is unknown — a limit nobody stated constrains nothing.
|
|
48
35
|
*/
|
|
49
|
-
|
|
50
|
-
/** Chord placement to follow; need not be pre-sorted. */
|
|
51
|
-
segments: readonly BassSegment[];
|
|
52
|
-
/** Key/scale context, used for diatonic approach tones in `walking`. */
|
|
53
|
-
key: KeyScale;
|
|
54
|
-
/**
|
|
55
|
-
* Time signature; used for metric accents.
|
|
56
|
-
*
|
|
57
|
-
* @defaultValue `{ numerator: 4, denominator: 4 }`
|
|
58
|
-
*/
|
|
59
|
-
ts?: TimeSignature;
|
|
60
|
-
/**
|
|
61
|
-
* Bass-line idiom.
|
|
62
|
-
*
|
|
63
|
-
* @defaultValue `'root'`
|
|
64
|
-
*/
|
|
65
|
-
style?: BassStyle;
|
|
66
|
-
/**
|
|
67
|
-
* Target register as a base MIDI octave; roots land around `octave*12+12`.
|
|
68
|
-
* An octave pickup in the `pop` style may drop a note into the octave below
|
|
69
|
-
* that band, which is where such a pickup belongs.
|
|
70
|
-
*
|
|
71
|
-
* @defaultValue 2
|
|
72
|
-
*/
|
|
73
|
-
octave?: number;
|
|
74
|
-
/**
|
|
75
|
-
* Seed for the deterministic PRNG.
|
|
76
|
-
*
|
|
77
|
-
* @defaultValue 0
|
|
78
|
-
*/
|
|
79
|
-
seed?: number;
|
|
80
|
-
};
|
|
36
|
+
declare function sustainsStrokes(stepBeats: number, bpm: number | undefined, difficulty: number | undefined): boolean;
|
|
81
37
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* Each segment contributes notes in the chosen bass register per the selected
|
|
85
|
-
* style; consecutive notes are kept within roughly a fifth. Every note's
|
|
86
|
-
* duration extends to the next onset (the final note extends to the last
|
|
87
|
-
* segment's end). Given a seed the output is fully reproducible.
|
|
88
|
-
*
|
|
89
|
-
* @param opts Segments, key, and generation options.
|
|
90
|
-
* @returns Bass notes sorted by onset, non-overlapping.
|
|
38
|
+
* Whether the fretting hand covers a leap in the time available.
|
|
91
39
|
*
|
|
92
|
-
* @
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* ];
|
|
99
|
-
* const notes = generateBassLine({ segments, key: majorKey(0), style: 'walking' });
|
|
100
|
-
* ```
|
|
101
|
-
*
|
|
102
|
-
* @category Composition
|
|
40
|
+
* @param semitones Distance moved; on a fretted neck one fret is one semitone.
|
|
41
|
+
* @param stepBeats Time to move it in, in quarter-note beats.
|
|
42
|
+
* @param bpm Tempo, or undefined when the caller gave none.
|
|
43
|
+
* @param difficulty The ceiling, or undefined for no ceiling.
|
|
44
|
+
* @returns True when the shift is inside the ceiling, and whenever either the
|
|
45
|
+
* ceiling or the tempo is unknown.
|
|
103
46
|
*/
|
|
104
|
-
declare function
|
|
47
|
+
declare function sustainsShift(semitones: number, stepBeats: number, bpm: number | undefined, difficulty: number | undefined): boolean;
|
|
105
48
|
|
|
106
49
|
/**
|
|
107
|
-
*
|
|
50
|
+
* The bass lick dictionary: the figures a bass player reaches for, held as
|
|
51
|
+
* chord-relative degrees and beat positions and deformed to fit the progression
|
|
52
|
+
* actually in front of them.
|
|
108
53
|
*
|
|
109
|
-
*
|
|
54
|
+
* Provenance: no entry reproduces a bass line from a particular song. Each is a
|
|
55
|
+
* figure that belongs to nobody — the descending soul walk-down, the octave
|
|
56
|
+
* slap, the boogie shuffle — of the kind every player of that music already
|
|
57
|
+
* plays, and each records the ground it qualifies under.
|
|
110
58
|
*/
|
|
111
|
-
type CounterMelodyOptions = {
|
|
112
|
-
/** The lead line to write against, in ascending onset order. */
|
|
113
|
-
melody: readonly NoteEvent[];
|
|
114
|
-
/**
|
|
115
|
-
* The harmony to write against. A timeline carries its own segment
|
|
116
|
-
* boundaries, so a chord change anywhere — including off the generator's
|
|
117
|
-
* half-beat probe grid — is seen; prefer it over `chordAt`.
|
|
118
|
-
*
|
|
119
|
-
* Exactly one of `timeline` and `chordAt` is required; `timeline` wins when
|
|
120
|
-
* both are given.
|
|
121
|
-
*/
|
|
122
|
-
timeline?: ChordTimeline;
|
|
123
|
-
/**
|
|
124
|
-
* Chord sounding at an absolute beat position, or null when none applies.
|
|
125
|
-
*
|
|
126
|
-
* A callback is opaque: the generator cannot see where it changes, so it
|
|
127
|
-
* probes a half-beat grid and otherwise relies on `chordChangeBeats`. A held
|
|
128
|
-
* counter note that spans an unseen change is not checked against the new
|
|
129
|
-
* chord.
|
|
130
|
-
*/
|
|
131
|
-
chordAt?: (beat: number) => Chord | null;
|
|
132
|
-
/**
|
|
133
|
-
* Exact chord-change beats the `chordAt` callback does not expose. Ignored
|
|
134
|
-
* when `timeline` is given, since a timeline lists its own boundaries.
|
|
135
|
-
*/
|
|
136
|
-
chordChangeBeats?: number[];
|
|
137
|
-
/** Key/scale context for scale-tone decisions. */
|
|
138
|
-
key: KeyScale;
|
|
139
|
-
/**
|
|
140
|
-
* Meter used for strong-beat decisions.
|
|
141
|
-
*
|
|
142
|
-
* @defaultValue 4/4
|
|
143
|
-
*/
|
|
144
|
-
ts?: TimeSignature;
|
|
145
|
-
/**
|
|
146
|
-
* Which side of the melody the counter line occupies.
|
|
147
|
-
*
|
|
148
|
-
* @defaultValue 'below'
|
|
149
|
-
*/
|
|
150
|
-
register?: 'above' | 'below';
|
|
151
|
-
/**
|
|
152
|
-
* Onset strategy: `'complement'` moves where the melody holds or rests and
|
|
153
|
-
* reinforces some strong beats; `'follow'` mirrors melody onsets.
|
|
154
|
-
*
|
|
155
|
-
* @defaultValue 'complement'
|
|
156
|
-
*/
|
|
157
|
-
rhythm?: 'complement' | 'follow';
|
|
158
|
-
/**
|
|
159
|
-
* Safety profile applied to candidate pitches.
|
|
160
|
-
*
|
|
161
|
-
* @defaultValue 'pop'
|
|
162
|
-
*/
|
|
163
|
-
profile?: 'strict' | 'pop';
|
|
164
|
-
/** Lowest MIDI pitch the counter line may use (default derived from `register`). */
|
|
165
|
-
pitchLow?: number;
|
|
166
|
-
/** Highest MIDI pitch the counter line may use (default derived from `register`). */
|
|
167
|
-
pitchHigh?: number;
|
|
168
|
-
/**
|
|
169
|
-
* PRNG seed. It places the `'complement'` onsets and breaks ties between
|
|
170
|
-
* equally good candidate pitches; the same seed always yields the same line.
|
|
171
|
-
* Where the constraints leave only one good answer — which is common with
|
|
172
|
-
* `rhythm: 'follow'`, whose onsets are fixed by the melody — changing the seed
|
|
173
|
-
* changes nothing.
|
|
174
|
-
*
|
|
175
|
-
* @defaultValue 0
|
|
176
|
-
*/
|
|
177
|
-
seed?: number;
|
|
178
|
-
};
|
|
179
|
-
/**
|
|
180
|
-
* Generate a counter melody against a lead line.
|
|
181
|
-
*
|
|
182
|
-
* Onsets are chosen per `rhythm`: `'follow'` places one counter note on each
|
|
183
|
-
* melody onset; `'complement'` fills melody rests and sustains and reinforces
|
|
184
|
-
* an occasional strong beat, spaced at least a beat apart. Each onset's pitch
|
|
185
|
-
* is picked from the safe pitches in the counter register (chord tones and
|
|
186
|
-
* consonant tensions against the sounding melody note), rejecting candidates
|
|
187
|
-
* that would form a parallel perfect interval with the melody or sit on the
|
|
188
|
-
* wrong side of it, then scored to favour contrary or oblique motion, imperfect
|
|
189
|
-
* consonance, and stepwise movement. Ties are broken by a seeded perturbation far
|
|
190
|
-
* below any real score difference (see `TIE_BREAK_JITTER`), so the same
|
|
191
|
-
* seed always yields the same line while a different seed can only reshuffle
|
|
192
|
-
* candidates that were already equally good. Notes extend to the next counter
|
|
193
|
-
* onset (the last to the melody's end) at a velocity slightly under the melody.
|
|
194
|
-
*
|
|
195
|
-
* @param opts The lead line, chord context callback, key, and generation knobs.
|
|
196
|
-
* @returns The counter line as note events sorted by onset; `[]` for an empty melody.
|
|
197
|
-
* @example
|
|
198
|
-
* ```ts
|
|
199
|
-
* import { generateCounterMelody, majorKey, parseChordSymbol } from '@libraz/libcantus';
|
|
200
|
-
* const melody = [{ pitch: 72, startBeat: 0, durationBeat: 2 }];
|
|
201
|
-
* const counter = generateCounterMelody({
|
|
202
|
-
* melody,
|
|
203
|
-
* chordAt: () => parseChordSymbol('C'),
|
|
204
|
-
* key: majorKey(0),
|
|
205
|
-
* }); // note events below the melody, in onset order
|
|
206
|
-
* ```
|
|
207
|
-
* @category Voicing & Counterpoint
|
|
208
|
-
*/
|
|
209
|
-
declare function generateCounterMelody(opts: CounterMelodyOptions): NoteEvent[];
|
|
210
59
|
|
|
211
60
|
/**
|
|
212
|
-
*
|
|
61
|
+
* One note of a lick, relative to the chord it is played over.
|
|
213
62
|
*
|
|
214
|
-
* A
|
|
215
|
-
*
|
|
216
|
-
* are
|
|
217
|
-
*
|
|
63
|
+
* A degree is 1-based and counts up from the chord's root: 1 is the root, 3 the
|
|
64
|
+
* third, 8 the octave. Degrees the chord itself names — the third, fifth and
|
|
65
|
+
* seventh — are taken from the chord, so the same figure comes out major over a
|
|
66
|
+
* major chord and minor over a minor one; the rest are taken from the key. That
|
|
67
|
+
* is what lets a figure be written once and fit a whole progression.
|
|
218
68
|
*
|
|
219
|
-
* @example
|
|
220
|
-
* ```ts
|
|
221
|
-
* import { DRUM_NOTES, generateDrums } from '@libraz/libcantus';
|
|
222
|
-
* const hits = generateDrums({ bars: 1, bpm: 120, style: 'standard', section: 'verse', density: 0.5 });
|
|
223
|
-
* const kick = hits.filter((hit) => hit.pitch === DRUM_NOTES.kick);
|
|
224
|
-
* ```
|
|
225
69
|
* @category Composition
|
|
226
70
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
readonly ride: 51;
|
|
237
|
-
readonly tambourine: 54;
|
|
238
|
-
readonly highTom: 50;
|
|
239
|
-
readonly midTom: 47;
|
|
240
|
-
readonly lowTom: 45;
|
|
241
|
-
readonly shaker: 82;
|
|
71
|
+
type LickNote = GridEvent & {
|
|
72
|
+
/** Chord degree, 1-based; 8 is the octave above the root. */
|
|
73
|
+
degree: number;
|
|
74
|
+
/** Chromatic alteration in semitones, for a flattened or raised degree. */
|
|
75
|
+
alter?: number;
|
|
76
|
+
/** Sounding length in sixteenths; one step when absent. */
|
|
77
|
+
lengthSteps?: number;
|
|
78
|
+
/** How the note is played, when it is more than a plain note. */
|
|
79
|
+
articulation?: Articulation;
|
|
242
80
|
};
|
|
243
81
|
/**
|
|
244
|
-
* A
|
|
245
|
-
*
|
|
246
|
-
* @category Composition
|
|
247
|
-
*/
|
|
248
|
-
type DrumVoice = keyof typeof DRUM_NOTES;
|
|
249
|
-
/**
|
|
250
|
-
* The voice a percussion note number names.
|
|
82
|
+
* A lick: its notes and how long the whole figure lasts.
|
|
251
83
|
*
|
|
252
|
-
* @param pitch The General MIDI note number.
|
|
253
|
-
* @returns The voice, or undefined for a note this generator never emits.
|
|
254
84
|
* @category Composition
|
|
255
85
|
*/
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
* @category Composition
|
|
261
|
-
*/
|
|
262
|
-
type DrumHit = {
|
|
263
|
-
/** General MIDI note number; {@link drumVoiceOf} names it. */
|
|
264
|
-
pitch: number;
|
|
265
|
-
startBeat: number;
|
|
266
|
-
durationBeat: number;
|
|
267
|
-
velocity: number;
|
|
86
|
+
type LickMaterial = {
|
|
87
|
+
/** Length of the figure in sixteenths. */
|
|
88
|
+
lengthSteps: number;
|
|
89
|
+
notes: LickNote[];
|
|
268
90
|
};
|
|
269
|
-
|
|
270
|
-
/** Every groove feel, in declaration order. */
|
|
271
|
-
declare const DRUM_FEELS: readonly ["straight", "swing", "shuffle"];
|
|
272
|
-
/** Groove feel controlling off-beat swing. */
|
|
273
|
-
type Feel = (typeof DRUM_FEELS)[number];
|
|
274
|
-
/**
|
|
275
|
-
* Every public groove style, in declaration order. The type is derived from
|
|
276
|
-
* this list so the runtime guard and the type can never disagree.
|
|
277
|
-
*/
|
|
278
|
-
declare const GROOVE_STYLES: readonly ["standard", "funk", "shuffle", "bossa", "trap", "halftime", "breakbeat", "house", "synthpop"];
|
|
279
91
|
/**
|
|
280
|
-
*
|
|
92
|
+
* A lick with the conditions it fits.
|
|
281
93
|
*
|
|
282
94
|
* @category Composition
|
|
283
95
|
*/
|
|
284
|
-
type
|
|
285
|
-
/** Every public role, in declaration order. */
|
|
286
|
-
declare const DRUM_ROLES: readonly ["full", "ambient", "minimal", "fxOnly"];
|
|
96
|
+
type BassLick = Vocabulary<LickMaterial>;
|
|
287
97
|
/**
|
|
288
|
-
*
|
|
98
|
+
* Whether a caller's material is a bass lick.
|
|
289
99
|
*
|
|
290
100
|
* @category Composition
|
|
291
101
|
*/
|
|
292
|
-
|
|
293
|
-
/** Every public section, in declaration order. */
|
|
294
|
-
declare const PUBLIC_SECTIONS: readonly ["intro", "verse", "prechorus", "chorus", "bridge", "outro"];
|
|
295
|
-
/** Public section identifiers. */
|
|
296
|
-
type PublicSection = (typeof PUBLIC_SECTIONS)[number];
|
|
297
|
-
|
|
102
|
+
declare function isLickMaterial(material: unknown): material is LickMaterial;
|
|
298
103
|
/**
|
|
299
|
-
*
|
|
104
|
+
* The built-in bass licks, in declaration order.
|
|
300
105
|
*
|
|
301
106
|
* @category Composition
|
|
302
107
|
*/
|
|
303
|
-
|
|
304
|
-
|
|
108
|
+
declare const BASS_LICKS: readonly BassLick[];
|
|
305
109
|
/**
|
|
306
|
-
*
|
|
110
|
+
* Options controlling {@link placeLicks}.
|
|
307
111
|
*
|
|
308
112
|
* @category Composition
|
|
309
113
|
*/
|
|
310
|
-
type
|
|
311
|
-
/**
|
|
312
|
-
* A Euclidean (Bjorklund) kick pattern for {@link generateDrums}.
|
|
313
|
-
*
|
|
314
|
-
* @category Composition
|
|
315
|
-
*/
|
|
316
|
-
type EuclideanKick = {
|
|
317
|
-
/** Number of kick onsets, clamped to `[0, steps]`. */
|
|
318
|
-
pulses: number;
|
|
319
|
-
/**
|
|
320
|
-
* Total steps in the bar (1..16).
|
|
321
|
-
*
|
|
322
|
-
* @defaultValue 16
|
|
323
|
-
*/
|
|
324
|
-
steps?: number;
|
|
114
|
+
type PlaceLicksOptions = {
|
|
325
115
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* @defaultValue 0
|
|
116
|
+
* The genre whose figures may be used. Genre is what selects the material:
|
|
117
|
+
* the dials then deform it, and the ceiling rejects it.
|
|
329
118
|
*/
|
|
330
|
-
|
|
331
|
-
};
|
|
332
|
-
/**
|
|
333
|
-
* Options controlling {@link generateDrums}.
|
|
334
|
-
*
|
|
335
|
-
* @category Composition
|
|
336
|
-
*/
|
|
337
|
-
type DrumsOptions = {
|
|
119
|
+
genre: Genre;
|
|
338
120
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
* count shared with a meter-aware generator such as
|
|
342
|
-
* {@link generateRhythm} only lines up when that generator is also in 4/4.
|
|
121
|
+
* The hardest figure that may be used, 1 to 5. Sugar for
|
|
122
|
+
* `ctx: { complexity: { difficulty } }`; the context wins over both.
|
|
343
123
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
section: Section;
|
|
348
|
-
density: number;
|
|
124
|
+
difficulty?: number;
|
|
125
|
+
/** Time signature; defaults to 4/4. */
|
|
126
|
+
ts?: TimeSignature;
|
|
349
127
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* A pre-chorus leading into a chorus builds instead: the two-bar lift takes
|
|
353
|
-
* precedence over the fill, since the buildup already marks the phrase end.
|
|
354
|
-
* Set `nextSection` to anything but `'chorus'` to get the fill.
|
|
128
|
+
* Target register as a base MIDI octave; roots land around `octave*12+12`.
|
|
355
129
|
*
|
|
356
|
-
* @defaultValue
|
|
130
|
+
* @defaultValue 2
|
|
357
131
|
*/
|
|
358
|
-
|
|
359
|
-
|
|
132
|
+
octave?: number;
|
|
133
|
+
/** The instrument the line is written for; its range always applies. */
|
|
134
|
+
instrument?: StringedProfile;
|
|
360
135
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
* - `'ambient'`: side-stick backbeat, ride instead of hi-hat.
|
|
365
|
-
* - `'minimal'`: side-stick backbeat quieter still, pedal hi-hat, no
|
|
366
|
-
* auxiliary percussion.
|
|
367
|
-
* - `'fxOnly'`: no kick, snare, ghost notes, timekeeping hi-hat, or fills —
|
|
368
|
-
* only the fx and auxiliary voices.
|
|
136
|
+
* The generation context. Its `vocabulary` brings the caller's own figures,
|
|
137
|
+
* its `complexity.ornament` decides how much decoration survives, and its
|
|
138
|
+
* `complexity.difficulty` is the ceiling figures are rejected against.
|
|
369
139
|
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
140
|
+
* Its `complexity.rhythmic` is how busy the line is, in [0, 1]: it decides
|
|
141
|
+
* both how often a segment takes a figure at all and, above its middle
|
|
142
|
+
* setting, how much the figure is syncopated — one dial, because "less busy"
|
|
143
|
+
* means both. Its `bpm` is the tempo the ceiling is measured against, and its
|
|
144
|
+
* `seed` fixes which figures are chosen and where they land.
|
|
372
145
|
*
|
|
373
|
-
* @defaultValue `
|
|
146
|
+
* @defaultValue `{ seed: 0, bpm: 120, complexity: { rhythmic: 0.6 } }`
|
|
374
147
|
*/
|
|
375
|
-
|
|
148
|
+
ctx?: GenerationContextInput;
|
|
376
149
|
/**
|
|
377
|
-
*
|
|
150
|
+
* Maximum number of segments {@link placeLicks} may lay figures over. One
|
|
151
|
+
* figure is placed per segment, so this is the guard against an unbounded
|
|
152
|
+
* caller rather than a limit on any search.
|
|
378
153
|
*
|
|
379
|
-
* @defaultValue
|
|
380
|
-
*/
|
|
381
|
-
seed?: number;
|
|
382
|
-
/**
|
|
383
|
-
* Section the final-bar fill leads into. Shapes which fill archetype is
|
|
384
|
-
* chosen (into-chorus and out-of-intro fills differ from generic ones).
|
|
385
|
-
* Defaults to `section`, i.e. a within-section fill.
|
|
386
|
-
*/
|
|
387
|
-
nextSection?: Section;
|
|
388
|
-
/**
|
|
389
|
-
* When set, the kick follows this Euclidean rhythm instead of the
|
|
390
|
-
* style/section pattern, giving direct access to evenly spread onsets.
|
|
154
|
+
* @defaultValue 1000000
|
|
391
155
|
*/
|
|
392
|
-
|
|
156
|
+
budget?: number;
|
|
393
157
|
};
|
|
394
158
|
/**
|
|
395
|
-
*
|
|
159
|
+
* Lay licks over a chord placement.
|
|
396
160
|
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* by
|
|
400
|
-
*
|
|
401
|
-
* and
|
|
402
|
-
*
|
|
403
|
-
* shaped by `nextSection`; a fill that would emit nothing on its beat falls back
|
|
404
|
-
* to the normal groove so the phrase end is never silent. `euclideanKick`
|
|
405
|
-
* overrides the kick with an evenly-spread Euclidean pattern. Output is fully
|
|
406
|
-
* determined by the options plus `seed`.
|
|
161
|
+
* Each segment is offered the figures whose conditions it meets — the genre,
|
|
162
|
+
* the chord quality, the tempo, and the difficulty ceiling — and one of them is
|
|
163
|
+
* chosen by a draw addressed to that segment, so changing a chord elsewhere
|
|
164
|
+
* leaves this segment's figure where it was. A segment that takes no figure
|
|
165
|
+
* gets its root, and the last note before a chord change leads into the next
|
|
166
|
+
* root by step, the way a walking line does.
|
|
407
167
|
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
* @param opts
|
|
413
|
-
* @returns
|
|
168
|
+
* @param timeline Chord segments to play over; they need not be pre-sorted, and
|
|
169
|
+
* each segment's chord may be written as a chord symbol.
|
|
170
|
+
* @param key Key context, which answers for the degrees the chords do not; a
|
|
171
|
+
* key name such as `'C major'` is read as that key.
|
|
172
|
+
* @param opts Genre, density, ceiling and seed.
|
|
173
|
+
* @returns Bass notes sorted by onset, non-overlapping.
|
|
174
|
+
* @throws If a segment has a non-positive duration, or the genre is not one
|
|
175
|
+
* this library names.
|
|
414
176
|
*
|
|
415
177
|
* @example
|
|
416
178
|
* ```ts
|
|
417
|
-
* import {
|
|
418
|
-
* const
|
|
419
|
-
*
|
|
179
|
+
* import { majorKey, makeChord, placeLicks } from '@libraz/libcantus';
|
|
180
|
+
* const timeline = [
|
|
181
|
+
* { startBeat: 0, endBeat: 4, chord: makeChord(0, 'maj7') },
|
|
182
|
+
* { startBeat: 4, endBeat: 8, chord: makeChord(5, 'maj7') },
|
|
183
|
+
* ];
|
|
184
|
+
* const notes = placeLicks(timeline, majorKey(0), { genre: 'motown', ctx: { seed: 7 } });
|
|
420
185
|
* ```
|
|
421
186
|
*
|
|
422
187
|
* @category Composition
|
|
423
188
|
*/
|
|
424
|
-
declare function
|
|
189
|
+
declare function placeLicks(timeline: readonly (Omit<ChordSegment, 'chord'> & {
|
|
190
|
+
chord: ChordLike;
|
|
191
|
+
})[], key: KeyLike, opts: PlaceLicksOptions): NoteEvent[];
|
|
425
192
|
|
|
426
193
|
/**
|
|
427
|
-
*
|
|
428
|
-
* feel, and capturing/replaying the timing-and-velocity "feel" of a
|
|
429
|
-
* performance as a reusable groove template.
|
|
194
|
+
* Imitative entries: a second voice restating what the first has just sung.
|
|
430
195
|
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
196
|
+
* A motif transform has no time relationship to anything — it returns a cell,
|
|
197
|
+
* and where that cell goes is the caller's problem. An imitation is defined by
|
|
198
|
+
* that relationship: it enters at a stated beat, at a stated interval, against a
|
|
199
|
+
* voice that is still sounding. That is what a canon is, and what the answering
|
|
200
|
+
* phrase in the second half of a chorus is.
|
|
434
201
|
*/
|
|
435
202
|
|
|
203
|
+
/** Every way an imitation can answer, in declaration order. */
|
|
204
|
+
declare const IMITATION_ANSWERS: readonly ["real", "tonal"];
|
|
205
|
+
/**
|
|
206
|
+
* How an imitation answers the voice it follows.
|
|
207
|
+
*
|
|
208
|
+
* A `real` answer transposes literally, keeping every interval of the subject
|
|
209
|
+
* and leaving the key where the interval puts it. A `tonal` answer counts scale
|
|
210
|
+
* degrees instead, so the answer stays in the key and adjusts an interval or two
|
|
211
|
+
* to do it — the fifth of a subject answered by a fourth.
|
|
212
|
+
*
|
|
213
|
+
* @category Composition
|
|
214
|
+
*/
|
|
215
|
+
type ImitationAnswer = (typeof IMITATION_ANSWERS)[number];
|
|
436
216
|
/**
|
|
437
|
-
* Options controlling {@link
|
|
217
|
+
* Options controlling {@link imitate}.
|
|
438
218
|
*
|
|
439
|
-
* @category
|
|
219
|
+
* @category Composition
|
|
440
220
|
*/
|
|
441
|
-
type
|
|
221
|
+
type ImitationOptions = {
|
|
442
222
|
/**
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
* @defaultValue 4/4
|
|
223
|
+
* Beat at which the imitation enters; the copied material starts here.
|
|
224
|
+
* Negative like any other onset, for an answer that enters in a pickup.
|
|
446
225
|
*/
|
|
447
|
-
|
|
226
|
+
atBeat: number;
|
|
448
227
|
/**
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
* @defaultValue 0.02
|
|
228
|
+
* Interval to imitate at, spelled: `'P5'` for an answer a fifth above,
|
|
229
|
+
* `'-P4'` for one a fourth below.
|
|
452
230
|
*/
|
|
453
|
-
|
|
231
|
+
interval: IntervalLike;
|
|
454
232
|
/**
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
* @defaultValue 8
|
|
233
|
+
* The key the answer is measured in; what makes a tonal answer tonal. A key
|
|
234
|
+
* name such as `'C major'` is read as that key.
|
|
458
235
|
*/
|
|
459
|
-
|
|
236
|
+
key: KeyLike;
|
|
460
237
|
/**
|
|
461
|
-
*
|
|
238
|
+
* Whether the answer transposes literally or by scale degree.
|
|
462
239
|
*
|
|
463
|
-
* @defaultValue
|
|
240
|
+
* @defaultValue 'real'
|
|
464
241
|
*/
|
|
465
|
-
|
|
242
|
+
answer?: ImitationAnswer;
|
|
466
243
|
/**
|
|
467
|
-
*
|
|
244
|
+
* Turn the subject upside down before transposing it, mirroring it about its
|
|
245
|
+
* own first note: by semitone for a real answer, by scale degree for a tonal
|
|
246
|
+
* one.
|
|
468
247
|
*
|
|
469
|
-
* @defaultValue
|
|
248
|
+
* @defaultValue false
|
|
470
249
|
*/
|
|
471
|
-
|
|
250
|
+
invert?: boolean;
|
|
251
|
+
/** First beat of the lead to imitate; defaults to the start of the line. */
|
|
252
|
+
from?: number;
|
|
253
|
+
/** Beat to imitate up to, exclusive; defaults to the end of the line. */
|
|
254
|
+
to?: number;
|
|
472
255
|
/**
|
|
473
|
-
*
|
|
256
|
+
* Factor applied to the copied velocities, for an answer that sits under the
|
|
257
|
+
* voice it follows.
|
|
474
258
|
*
|
|
475
|
-
* @defaultValue
|
|
259
|
+
* @defaultValue 1
|
|
476
260
|
*/
|
|
477
|
-
|
|
261
|
+
velocityScale?: number;
|
|
478
262
|
};
|
|
479
263
|
/**
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* @param
|
|
496
|
-
* @
|
|
264
|
+
* Answer a leading voice with an imitation of it.
|
|
265
|
+
*
|
|
266
|
+
* The chosen span of the lead is copied so that its first note falls on
|
|
267
|
+
* `atBeat`, keeping its internal rhythm; only the answer is returned, so the
|
|
268
|
+
* caller decides what to do with the two voices together. Pitches move by
|
|
269
|
+
* `interval`: literally for a real answer, by scale degree for a tonal one, and
|
|
270
|
+
* upside down first when `invert` is set. Inversion mirrors about the first note
|
|
271
|
+
* of the copied span, so the answer starts where the transposition puts that
|
|
272
|
+
* note either way. In a tonal answer a pitch outside the key keeps its distance
|
|
273
|
+
* from the scale tone below it, and inversion mirrors that distance too — a
|
|
274
|
+
* chromatic passing note stays a chromatic passing note, on the other side.
|
|
275
|
+
* Where a mirrored chromatic note falls into a diatonic semitone the key leaves
|
|
276
|
+
* it no room, and it lands on the scale tone there, so a subject saturated with
|
|
277
|
+
* chromatic steps can answer with a pitch repeated.
|
|
278
|
+
*
|
|
279
|
+
* @param lead The voice being imitated, in ascending onset order.
|
|
280
|
+
* @param opts Where the answer enters, at what interval, and in what key.
|
|
281
|
+
* @returns The imitation as note events sorted by onset; `[]` when the chosen
|
|
282
|
+
* span holds no sounding notes. Notes with a zero or negative duration never
|
|
283
|
+
* sound and are not copied, so the answer can be shorter than the span.
|
|
284
|
+
* @throws If the span is reversed, the answer is not a known kind, or a
|
|
285
|
+
* transposed pitch leaves the MIDI range.
|
|
497
286
|
* @example
|
|
498
287
|
* ```ts
|
|
499
|
-
* import {
|
|
500
|
-
* const
|
|
501
|
-
*
|
|
288
|
+
* import { imitate, majorKey } from '@libraz/libcantus';
|
|
289
|
+
* const lead = [0, 1, 2].map((beat) => ({ pitch: 60 + beat, startBeat: beat, durationBeat: 1 }));
|
|
290
|
+
* imitate(lead, { atBeat: 2, interval: 'P5', key: majorKey(0) });
|
|
291
|
+
* // the same three notes a fifth up, entering at beat 2
|
|
502
292
|
* ```
|
|
503
|
-
*
|
|
504
|
-
* result can be shorter than the input.
|
|
505
|
-
*
|
|
506
|
-
* @category Rhythm & Meter
|
|
293
|
+
* @category Composition
|
|
507
294
|
*/
|
|
508
|
-
declare function
|
|
295
|
+
declare function imitate(lead: readonly NoteEvent[], opts: ImitationOptions): NoteEvent[];
|
|
296
|
+
|
|
509
297
|
/**
|
|
510
|
-
* The
|
|
298
|
+
* The fill vocabulary, as data.
|
|
511
299
|
*
|
|
512
|
-
*
|
|
300
|
+
* Every archetype is a list of strokes on the bar's grid and every selection is
|
|
301
|
+
* a table indexed by a draw, so a fill can be added, replaced, or supplied by a
|
|
302
|
+
* caller without touching a generator. The generator's part is to look one up.
|
|
513
303
|
*/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
304
|
+
|
|
305
|
+
/** The thirteen fill archetypes for section transitions, in declaration order. */
|
|
306
|
+
declare const FILL_TYPES: readonly ["snareRoll", "tomDescend", "tomAscend", "snareTomCombo", "simpleCrash", "linearFill", "ghostToAccent", "bdSnareAlternate", "hiHatChoke", "tomShuffle", "breakdownFill", "flamsAndDrags", "halfTimeFill"];
|
|
307
|
+
/** The thirteen fill archetypes for section transitions. */
|
|
308
|
+
type FillType = (typeof FILL_TYPES)[number];
|
|
309
|
+
/**
|
|
310
|
+
* How loud one stroke of a fill is.
|
|
311
|
+
*
|
|
312
|
+
* A fill's dynamics are written against the fill's own base velocity rather
|
|
313
|
+
* than in absolute terms, so the same archetype sounds right in a verse and a
|
|
314
|
+
* chorus: `fill` is the body of the fill, `accent` the stroke that lands. One
|
|
315
|
+
* base for the whole gesture, not one per beat — a crescendo that restarts its
|
|
316
|
+
* arithmetic at every beat line falls back in the middle of itself.
|
|
317
|
+
*/
|
|
318
|
+
type FillVelocity = {
|
|
319
|
+
base: 'fill' | 'accent';
|
|
320
|
+
/** Factor on the base, for a crescendo or a ghosted stroke. */
|
|
321
|
+
scale?: number;
|
|
322
|
+
/** Fixed offset in MIDI velocity, for a stroke a little above or below. */
|
|
323
|
+
offset?: number;
|
|
324
|
+
};
|
|
325
|
+
/** One stroke of a fill, positioned within the beat it belongs to. */
|
|
326
|
+
type FillStroke = {
|
|
327
|
+
/** General MIDI note number of the voice struck. */
|
|
328
|
+
voice: number;
|
|
329
|
+
/** Offset from the start of the beat, in quarter-note beats. */
|
|
330
|
+
offset: number;
|
|
331
|
+
/** Sounding length, in quarter-note beats. */
|
|
332
|
+
duration: number;
|
|
333
|
+
velocity: FillVelocity;
|
|
334
|
+
/** How the stroke is played, when it is more than a plain hit. */
|
|
335
|
+
articulation?: Articulation;
|
|
523
336
|
};
|
|
524
337
|
/**
|
|
525
|
-
* A
|
|
338
|
+
* A fill archetype: what each beat of the fill bar plays.
|
|
526
339
|
*
|
|
527
|
-
*
|
|
340
|
+
* Beats are indexed from the start of the bar. A beat with no entry is silent,
|
|
341
|
+
* which is how the sparse archetypes leave the earlier beats to the groove.
|
|
528
342
|
*/
|
|
529
|
-
type
|
|
530
|
-
/**
|
|
531
|
-
|
|
532
|
-
/** Number of grid slots per bar (`beatsPerBar(ts) * subdivision`). */
|
|
533
|
-
slotsPerBar: number;
|
|
534
|
-
/** One entry per grid slot, in slot-index order (slot 0 is the downbeat). */
|
|
535
|
-
slots: GrooveSlot[];
|
|
343
|
+
type FillArchetype = {
|
|
344
|
+
/** Strokes per beat of the bar, indexed 0..3. */
|
|
345
|
+
atBeat: readonly (readonly FillStroke[])[];
|
|
536
346
|
/**
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
* templates constructed without it (in which case no check is performed).
|
|
347
|
+
* Whether a bar longer than four beats keeps playing the last beat's strokes.
|
|
348
|
+
* The archetypes are written against a four-beat bar; most read naturally when
|
|
349
|
+
* extended, but the half-time figure is a bar-long gesture and stops there.
|
|
541
350
|
*/
|
|
542
|
-
|
|
351
|
+
boundToBar?: boolean;
|
|
543
352
|
};
|
|
544
353
|
/**
|
|
545
|
-
*
|
|
546
|
-
* the average timing deviation from the quantized grid and the average
|
|
547
|
-
* velocity of events landing on it. Slots with no velocity-carrying event
|
|
548
|
-
* record `velocity: null` (the "unrecorded" sentinel); empty slots default to
|
|
549
|
-
* `{ timingOffset: 0, velocity: null }`. The time signature is stored on the
|
|
550
|
-
* template so {@link applyGrooveTemplate} can verify the meter matches.
|
|
354
|
+
* The fill dictionary.
|
|
551
355
|
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
* template records — swing, push, drag — are audible.
|
|
557
|
-
* @returns The extracted groove template.
|
|
558
|
-
* @category Rhythm & Meter
|
|
356
|
+
* Provenance: every archetype here is a rudiment or a stock phrase-end gesture
|
|
357
|
+
* — a snare crescendo, a descending tom run, a crash on the last off-beat — of
|
|
358
|
+
* the kind every drummer plays and nobody owns. None reproduces a fill from a
|
|
359
|
+
* particular recording.
|
|
559
360
|
*/
|
|
560
|
-
declare
|
|
361
|
+
declare const FILL_ARCHETYPES: Readonly<Record<FillType, FillArchetype>>;
|
|
561
362
|
/**
|
|
562
|
-
*
|
|
563
|
-
* event is snapped to its grid slot and then offset by that slot's recorded
|
|
564
|
-
* timing deviation, pushing stiff timing toward the template's feel.
|
|
565
|
-
* Velocity is set to the slot's recorded average when it recorded one (a
|
|
566
|
-
* non-`null` velocity, including a genuine 0); otherwise the event's own
|
|
567
|
-
* velocity is left untouched.
|
|
568
|
-
*
|
|
569
|
-
* If the template carries a time signature (see {@link GrooveTemplate.ts}) it
|
|
570
|
-
* must match `ts`, since a differing bar length would misalign the per-bar
|
|
571
|
-
* grid and drift the feel; a mismatch throws rather than corrupting timing
|
|
572
|
-
* silently.
|
|
363
|
+
* Whether a caller's material is a fill.
|
|
573
364
|
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
* @returns Reshaped copies of `events`, in input order.
|
|
578
|
-
* @throws If `template.ts` is set and does not match `ts`.
|
|
579
|
-
* @example
|
|
580
|
-
* ```ts
|
|
581
|
-
* import { extractGrooveTemplate, applyGrooveTemplate, parseTimeSignature } from '@libraz/libcantus';
|
|
582
|
-
* const ts = parseTimeSignature('4/4');
|
|
583
|
-
* // Learn the feel from a loosely played performance...
|
|
584
|
-
* const groovy = [
|
|
585
|
-
* { pitch: 36, startBeat: 0.02, durationBeat: 1, velocity: 100 },
|
|
586
|
-
* { pitch: 38, startBeat: 1.06, durationBeat: 1, velocity: 70 },
|
|
587
|
-
* ];
|
|
588
|
-
* const template = extractGrooveTemplate(groovy, ts, 4);
|
|
589
|
-
* // ...then impose it on stiff, quantized events.
|
|
590
|
-
* const quantized = [{ pitch: 36, startBeat: 0, durationBeat: 1, velocity: 80 }];
|
|
591
|
-
* applyGrooveTemplate(quantized, template, ts); // events nudged toward the captured feel
|
|
592
|
-
* ```
|
|
593
|
-
* @category Rhythm & Meter
|
|
365
|
+
* A context carries one dictionary for the whole piece, so each generator has
|
|
366
|
+
* to recognise its own material; a bass lick reaching the fill selector would
|
|
367
|
+
* otherwise be rendered as silence.
|
|
594
368
|
*/
|
|
595
|
-
declare function
|
|
369
|
+
declare function isFillArchetype(material: unknown): material is FillArchetype;
|
|
596
370
|
|
|
597
371
|
/**
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
* @category Composition
|
|
601
|
-
*/
|
|
602
|
-
type ProgStyle = 'minimal' | 'dance' | 'idol' | 'rock';
|
|
603
|
-
/**
|
|
604
|
-
* Harmonic-function role of a progression as a whole.
|
|
372
|
+
* The kick vocabulary, as data on a sixteenth grid.
|
|
605
373
|
*
|
|
606
|
-
*
|
|
374
|
+
* The grid is sixteenths rather than eighths because most of what belongs in a
|
|
375
|
+
* kick dictionary cannot be written on eighths at all: the funk and modern-pop
|
|
376
|
+
* basics live on the sixteenths between the beats. The eighth-note patterns map
|
|
377
|
+
* onto the even steps of the same grid, so they read exactly as before.
|
|
607
378
|
*/
|
|
608
|
-
|
|
379
|
+
|
|
380
|
+
/** Sixteenth steps in one bar of 4/4; the grid a kick pattern is written on. */
|
|
381
|
+
declare const KICK_STEPS = 16;
|
|
382
|
+
/**
|
|
383
|
+
* Kick presence flags across the sixteen 16th-note slots of a bar.
|
|
384
|
+
*
|
|
385
|
+
* Index `n` is the `n`-th sixteenth from the downbeat, so beat 3 is index 8 and
|
|
386
|
+
* the "and" of 2 is index 6.
|
|
387
|
+
*/
|
|
388
|
+
type KickPattern = readonly boolean[];
|
|
389
|
+
/**
|
|
390
|
+
* One onset of a kick figure.
|
|
391
|
+
*
|
|
392
|
+
* A slot with no probability is part of the figure itself and always sounds.
|
|
393
|
+
* One with a probability is where the rhythmic dial reaches the kick: the draw
|
|
394
|
+
* is fixed by position and leaned on by the dial, so raising the dial fills
|
|
395
|
+
* slots in without emptying any that were already there.
|
|
396
|
+
*/
|
|
397
|
+
type KickSlot = {
|
|
398
|
+
/** Sixteenth index within the bar, 0..15. */
|
|
399
|
+
step: number;
|
|
400
|
+
/** Chance the slot is taken before the dial leans on it. */
|
|
401
|
+
probability?: number;
|
|
402
|
+
/** Draw address for the slot; slots that may sound must name one. */
|
|
403
|
+
slot?: string;
|
|
404
|
+
/** Restrict the slot to bars of one parity, for a two-bar figure. */
|
|
405
|
+
barParity?: 0 | 1;
|
|
406
|
+
/** Sections the slot is added in; absent means every section. */
|
|
407
|
+
sections?: Section[];
|
|
408
|
+
};
|
|
409
|
+
/** A style's kick figure: the slots it always plays, plus the ones it may. */
|
|
410
|
+
type KickFigure = readonly KickSlot[];
|
|
609
411
|
/**
|
|
610
|
-
*
|
|
412
|
+
* The kick dictionary, keyed by internal style.
|
|
611
413
|
*
|
|
612
|
-
*
|
|
414
|
+
* Provenance: each figure is the plain pulse of its style — the four-on-the-
|
|
415
|
+
* floor dance kick, the backbeat-answering "and" of two, the clave-leaning
|
|
416
|
+
* latin figure — none of them traceable to a particular record.
|
|
613
417
|
*/
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
name: string;
|
|
617
|
-
/**
|
|
618
|
-
* The chord roots, in order, as {@link ProgressionDegree} codes: 0..6 for the
|
|
619
|
-
* key's own scale degrees and {@link BORROWED_DEGREES} for the borrowed ones.
|
|
620
|
-
*/
|
|
621
|
-
degrees: ProgressionDegree[];
|
|
622
|
-
functional: ProgFunction;
|
|
623
|
-
styles: ProgStyle[];
|
|
624
|
-
};
|
|
418
|
+
declare const KICK_FIGURES: Readonly<Record<DrumStyle, KickFigure>>;
|
|
419
|
+
|
|
625
420
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
421
|
+
* A drum kit played with four limbs, covering every voice
|
|
422
|
+
* {@link generateDrums} emits.
|
|
628
423
|
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
424
|
+
* A kit is bounded by limbs rather than by range: two voices wanting the same
|
|
425
|
+
* hand at the same instant is what makes a pattern unplayable, and a voice no
|
|
426
|
+
* limb reaches is not on the kit at all.
|
|
632
427
|
*
|
|
633
428
|
* @example
|
|
634
429
|
* ```ts
|
|
635
|
-
* import {
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
* style: '
|
|
639
|
-
*
|
|
640
|
-
*
|
|
430
|
+
* import { DRUM_KIT, generateDrums, playability } from '@libraz/libcantus';
|
|
431
|
+
* const hits = generateDrums({
|
|
432
|
+
* bars: 1,
|
|
433
|
+
* style: 'funk',
|
|
434
|
+
* section: 'chorus',
|
|
435
|
+
* ctx: { bpm: 120, complexity: { rhythmic: 0.8 } },
|
|
641
436
|
* });
|
|
437
|
+
* const report = playability(hits, DRUM_KIT, 120);
|
|
642
438
|
* ```
|
|
643
439
|
* @category Composition
|
|
644
440
|
*/
|
|
645
|
-
declare const
|
|
646
|
-
|
|
647
|
-
readonly bVI: 8;
|
|
648
|
-
/** Flat subtonic: bVII. */
|
|
649
|
-
readonly bVII: 10;
|
|
650
|
-
/** Flat mediant: bIII. */
|
|
651
|
-
readonly bIII: 11;
|
|
652
|
-
/** Minor subdominant borrowed from the parallel minor: iv. */
|
|
653
|
-
readonly iv: 12;
|
|
654
|
-
/** Neapolitan: bII. */
|
|
655
|
-
readonly bII: 13;
|
|
656
|
-
/** Sharp subdominant, the diminished #IV. */
|
|
657
|
-
readonly sharpIV: 14;
|
|
658
|
-
};
|
|
441
|
+
declare const DRUM_KIT: PercussionProfile;
|
|
442
|
+
|
|
659
443
|
/**
|
|
660
|
-
*
|
|
661
|
-
*
|
|
444
|
+
* The drum pattern dictionary: the bar-long figures that make a groove belong
|
|
445
|
+
* to a genre rather than merely being correct.
|
|
662
446
|
*
|
|
663
|
-
*
|
|
447
|
+
* Provenance: every pattern here is the plain, unattributable pulse of its
|
|
448
|
+
* genre — the figure taught as "this is how that music goes" — written out on
|
|
449
|
+
* the sixteenth grid from that description. None reproduces the drum part of a
|
|
450
|
+
* particular recording, and each entry says which ground it qualifies under.
|
|
664
451
|
*/
|
|
665
|
-
|
|
452
|
+
|
|
666
453
|
/**
|
|
667
|
-
*
|
|
454
|
+
* One stroke of a pattern, on the bar's sixteenth grid.
|
|
668
455
|
*
|
|
669
|
-
*
|
|
456
|
+
* Velocity is a factor of the passage's base velocity rather than a MIDI
|
|
457
|
+
* number, so the same figure sits right in a verse and in a chorus.
|
|
670
458
|
*/
|
|
671
|
-
type
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
bars: number;
|
|
679
|
-
/** Pick a specific built-in preset by id instead of choosing one by style. */
|
|
680
|
-
presetId?: string;
|
|
681
|
-
/**
|
|
682
|
-
* Use this progression rather than a built-in one. Only `degrees` is
|
|
683
|
-
* required; see {@link BORROWED_DEGREES} for the non-diatonic codes.
|
|
684
|
-
*/
|
|
685
|
-
preset?: Partial<ProgressionPreset> & {
|
|
686
|
-
degrees: ProgressionDegree[];
|
|
687
|
-
};
|
|
688
|
-
ext?: ChordQuality | 'auto';
|
|
689
|
-
reharmonize?: boolean;
|
|
690
|
-
/**
|
|
691
|
-
* Seed for the deterministic preset choice and reharmonization.
|
|
692
|
-
*
|
|
693
|
-
* @defaultValue 0
|
|
694
|
-
*/
|
|
695
|
-
seed?: number;
|
|
459
|
+
type DrumStroke = GridEvent & {
|
|
460
|
+
voice: DrumVoice;
|
|
461
|
+
/** Sounding length in quarter-note beats; one sixteenth when absent. */
|
|
462
|
+
duration?: number;
|
|
463
|
+
articulation?: Articulation;
|
|
464
|
+
/** Which limb plays the stroke, where the figure depends on it. */
|
|
465
|
+
limb?: Limb;
|
|
696
466
|
};
|
|
697
467
|
/**
|
|
698
|
-
*
|
|
468
|
+
* A bar-long drum figure.
|
|
699
469
|
*
|
|
700
470
|
* @category Composition
|
|
701
471
|
*/
|
|
702
|
-
|
|
472
|
+
type DrumPattern = {
|
|
473
|
+
/** Sixteenth steps in the figure; a 4/4 bar is sixteen. */
|
|
474
|
+
steps: number;
|
|
475
|
+
strokes: DrumStroke[];
|
|
476
|
+
};
|
|
703
477
|
/**
|
|
704
|
-
*
|
|
478
|
+
* A drum figure with the conditions it fits.
|
|
705
479
|
*
|
|
706
480
|
* @category Composition
|
|
707
481
|
*/
|
|
708
|
-
|
|
482
|
+
type DrumVocabulary = Vocabulary<DrumPattern>;
|
|
709
483
|
/**
|
|
710
|
-
*
|
|
484
|
+
* Whether a caller's material is a drum pattern.
|
|
711
485
|
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
486
|
+
* A context carries one dictionary for the whole piece, so each generator
|
|
487
|
+
* recognises its own material: an entry meant for the bass is invisible here
|
|
488
|
+
* rather than mis-read.
|
|
715
489
|
*
|
|
716
|
-
* @param style The style pool to choose from.
|
|
717
|
-
* @param seed The same seed the generator would be given.
|
|
718
|
-
* @returns The preset that seed selects.
|
|
719
|
-
* @throws If no preset claims the style.
|
|
720
|
-
* @example
|
|
721
|
-
* ```ts
|
|
722
|
-
* import { pickProgressionPreset } from '@libraz/libcantus';
|
|
723
|
-
* pickProgressionPreset('dance', 3).name;
|
|
724
|
-
* ```
|
|
725
490
|
* @category Composition
|
|
726
491
|
*/
|
|
727
|
-
declare function
|
|
492
|
+
declare function isDrumPattern(material: unknown): material is DrumPattern;
|
|
728
493
|
/**
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
* A preset is chosen by `presetId` when given, otherwise deterministically from
|
|
732
|
-
* the presets matching `style`, seeded by `seed`. An unknown `presetId`, or a
|
|
733
|
-
* `style` no preset claims, is a caller error and throws rather than silently
|
|
734
|
-
* falling back to a random preset. The preset's degrees cycle to
|
|
735
|
-
* fill `bars`; each bar is four beats, so `startBeat` is `barIndex * 4`. Chord
|
|
736
|
-
* roots come from the key's diatonic scale-degree mapping. When `ext` is
|
|
737
|
-
* omitted or `'auto'`, each chord takes its diatonic triad quality; otherwise
|
|
738
|
-
* `ext` is forced on every chord — except a chord replaced by `reharmonize`,
|
|
739
|
-
* which is a secondary dominant and is therefore always a `dom7`; those carry
|
|
740
|
-
* `secondaryDominant: true`. When `reharmonize` is set, some chords are
|
|
741
|
-
* deterministically replaced with the secondary dominant (V7) of the following
|
|
742
|
-
* chord, flagged with `secondaryDominant`.
|
|
743
|
-
*
|
|
744
|
-
* @param opts Generation options.
|
|
745
|
-
* @returns One chord per bar in timeline order.
|
|
746
|
-
* @throws If `presetId` matches no built-in preset, or `style` matches none.
|
|
747
|
-
*
|
|
748
|
-
* @example
|
|
749
|
-
* ```ts
|
|
750
|
-
* import { generateProgression, majorKey } from '@libraz/libcantus';
|
|
751
|
-
* const chords = generateProgression({ key: majorKey(0), style: 'dance', bars: 4 });
|
|
752
|
-
* // Deterministic for a given seed (defaults to 0); one ChordSpan per bar.
|
|
753
|
-
* ```
|
|
494
|
+
* The built-in drum patterns, in declaration order.
|
|
754
495
|
*
|
|
755
496
|
* @category Composition
|
|
756
497
|
*/
|
|
757
|
-
declare
|
|
758
|
-
|
|
498
|
+
declare const DRUM_PATTERNS: readonly DrumVocabulary[];
|
|
759
499
|
/**
|
|
760
|
-
*
|
|
500
|
+
* Options controlling {@link placeDrumPattern}.
|
|
761
501
|
*
|
|
762
|
-
* @category
|
|
763
|
-
*/
|
|
764
|
-
type MelodyNote = NoteEvent;
|
|
765
|
-
/**
|
|
766
|
-
* Options controlling {@link harmonizeMelody}.
|
|
767
|
-
*
|
|
768
|
-
* @category Reharmonization
|
|
769
|
-
*/
|
|
770
|
-
type HarmonizePlacement = {
|
|
771
|
-
/**
|
|
772
|
-
* Search a range of transpositions and keep the best-fitting one. The result
|
|
773
|
-
* reports the shift it chose in `transposeSemitones`.
|
|
774
|
-
*
|
|
775
|
-
* @defaultValue false
|
|
776
|
-
*/
|
|
777
|
-
transposeSearch: boolean;
|
|
778
|
-
/**
|
|
779
|
-
* Search octave placements as well, so a melody written too high or too low
|
|
780
|
-
* is moved into a comfortable register.
|
|
781
|
-
*
|
|
782
|
-
* @defaultValue false
|
|
783
|
-
*/
|
|
784
|
-
octaveSearch: boolean;
|
|
785
|
-
};
|
|
786
|
-
/**
|
|
787
|
-
* Options controlling {@link harmonizeMelody}.
|
|
788
|
-
*
|
|
789
|
-
* Only `melody` is required; every other knob has the default a first call
|
|
790
|
-
* wants.
|
|
791
|
-
*
|
|
792
|
-
* @category Reharmonization
|
|
502
|
+
* @category Composition
|
|
793
503
|
*/
|
|
794
|
-
type
|
|
795
|
-
/**
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
* The key to harmonize in, or `'infer'` to estimate it from the melody's
|
|
799
|
-
* pitch-class weighting — which is what a caller who has only a melody
|
|
800
|
-
* wants, and so the default.
|
|
801
|
-
*
|
|
802
|
-
* @defaultValue `'infer'`
|
|
803
|
-
*/
|
|
804
|
-
key?: KeyScale | 'infer';
|
|
805
|
-
/**
|
|
806
|
-
* Length of each chord slot in beats. Any positive value is honoured, as in
|
|
807
|
-
* {@link chordTimelineFromNotes}; a value fine enough to make the search
|
|
808
|
-
* explode is rejected by the generation budget rather than rounded up.
|
|
809
|
-
*
|
|
810
|
-
* @defaultValue 2
|
|
811
|
-
*/
|
|
812
|
-
harmonicRhythm?: number;
|
|
504
|
+
type DrumPatternOptions = {
|
|
505
|
+
/** Number of bars to generate. */
|
|
506
|
+
bars: number;
|
|
813
507
|
/**
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
* wrong beats, and the error accumulates bar by bar.
|
|
817
|
-
*
|
|
818
|
-
* @defaultValue `4/4`
|
|
508
|
+
* The genre whose figures may be used. Genre is what selects the material;
|
|
509
|
+
* the complexity dials then deform it and the difficulty ceiling rejects it.
|
|
819
510
|
*/
|
|
511
|
+
genre: Genre;
|
|
512
|
+
/** Section the pattern plays in, matched against each entry's own sections. */
|
|
513
|
+
section?: Section;
|
|
514
|
+
/** Time signature; defaults to 4/4. */
|
|
820
515
|
ts?: TimeSignature;
|
|
821
516
|
/**
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
517
|
+
* The generation context. `bpm` is the tempo, `complexity.rhythmic` thins the
|
|
518
|
+
* figure below its middle setting and syncopates it above,
|
|
519
|
+
* `complexity.ornament` decides how many ghosts survive,
|
|
520
|
+
* `complexity.difficulty` rejects a figure the player could not keep up at
|
|
521
|
+
* this tempo, `vocabulary` brings figures of the caller's own, and `seed`
|
|
522
|
+
* fixes which figure is chosen for each bar.
|
|
825
523
|
*
|
|
826
|
-
* @defaultValue `
|
|
524
|
+
* @defaultValue `{ seed: 0, bpm: 120 }`
|
|
827
525
|
*/
|
|
828
|
-
|
|
526
|
+
ctx?: GenerationContextInput;
|
|
829
527
|
/**
|
|
830
|
-
*
|
|
831
|
-
*
|
|
528
|
+
* Play the figure at half or double its written rate. This is a deformation
|
|
529
|
+
* like the dials, not a different figure: the dictionary entry is the same.
|
|
832
530
|
*
|
|
833
|
-
*
|
|
834
|
-
|
|
835
|
-
placement?: HarmonizePlacement;
|
|
836
|
-
/**
|
|
837
|
-
* Seed for the deterministic tie-break perturbation.
|
|
531
|
+
* It is named for the note values it changes because `feel` names the swing
|
|
532
|
+
* the figure is played with, here and in {@link generateDrums} alike.
|
|
838
533
|
*
|
|
839
|
-
* @defaultValue
|
|
534
|
+
* @defaultValue `'straight'`
|
|
840
535
|
*/
|
|
841
|
-
|
|
842
|
-
};
|
|
843
|
-
/**
|
|
844
|
-
* The chosen transpose, key, chord path, and per-note roles.
|
|
845
|
-
*
|
|
846
|
-
* @category Reharmonization
|
|
847
|
-
*/
|
|
848
|
-
type HarmonizeResult = {
|
|
849
|
-
transposeSemitones: number;
|
|
850
|
-
key: KeyScale;
|
|
851
|
-
chords: ChordSpan[];
|
|
852
|
-
melodyRoles: {
|
|
853
|
-
noteIndex: number;
|
|
854
|
-
role: HarmonyRole;
|
|
855
|
-
}[];
|
|
856
|
-
};
|
|
857
|
-
/**
|
|
858
|
-
* Harmonize a melody with a min-cost chord progression.
|
|
859
|
-
*
|
|
860
|
-
* The melody is segmented by `harmonicRhythm`; each segment is scored against
|
|
861
|
-
* diatonic (and, per `reharmonize`, secondary-dominant and borrowed) candidate
|
|
862
|
-
* chords by melody fit, and a Viterbi search picks the lowest-cost path using a
|
|
863
|
-
* functional-flow transition cost. When `placement.transposeSearch`/`octaveSearch`
|
|
864
|
-
* is set, the whole melody is transposed across a range and the best
|
|
865
|
-
* `(transpose, progression)` pair is returned; a small tessitura cost breaks
|
|
866
|
-
* ties toward placements that keep the melody in a comfortable register.
|
|
867
|
-
*
|
|
868
|
-
* The `seed` drives a deterministic tie-break only: it perturbs candidates by a
|
|
869
|
-
* magnitude far below any real cost difference (see `TIE_BREAK_JITTER`),
|
|
870
|
-
* so it can decide between chords or paths of otherwise-equal cost but never
|
|
871
|
-
* overrides melody fit or functional flow. For a well-determined melody the
|
|
872
|
-
* result is identical across seeds; the same seed always yields the same result.
|
|
873
|
-
*
|
|
874
|
-
* @param opts The melody and, optionally, the key, harmonic rhythm,
|
|
875
|
-
* reharmonization strength, placement search, meter, and seed. Only `melody`
|
|
876
|
-
* is required.
|
|
877
|
-
* @returns The chosen transpose, key, chord path, and per-note roles.
|
|
878
|
-
* @example
|
|
879
|
-
* ```ts
|
|
880
|
-
* import { harmonizeMelody } from '@libraz/libcantus';
|
|
881
|
-
* const result = harmonizeMelody({
|
|
882
|
-
* melody: [
|
|
883
|
-
* { pitch: 60, startBeat: 0, durationBeat: 1 },
|
|
884
|
-
* { pitch: 64, startBeat: 1, durationBeat: 1 },
|
|
885
|
-
* ],
|
|
886
|
-
* });
|
|
887
|
-
* result.chords; // one ChordSpan per harmonic-rhythm segment
|
|
888
|
-
* ```
|
|
889
|
-
* @category Reharmonization
|
|
890
|
-
*/
|
|
891
|
-
declare function harmonizeMelody(opts: HarmonizeOptions): HarmonizeResult;
|
|
892
|
-
|
|
893
|
-
/**
|
|
894
|
-
* A transformation applicable to a motif cell.
|
|
895
|
-
*
|
|
896
|
-
* @category Composition
|
|
897
|
-
*/
|
|
898
|
-
type MotifTransform = 'transposeDiatonic' | 'transposeChromatic' | 'invert' | 'retrograde' | 'augment' | 'diminish' | 'sequence';
|
|
899
|
-
/**
|
|
900
|
-
* A single note within a motif cell.
|
|
901
|
-
*
|
|
902
|
-
* @category Composition
|
|
903
|
-
*/
|
|
904
|
-
type MotifNote = {
|
|
905
|
-
pitch: number;
|
|
906
|
-
startBeat: number;
|
|
907
|
-
durationBeat: number;
|
|
908
|
-
};
|
|
909
|
-
/**
|
|
910
|
-
* A short melodic cell.
|
|
911
|
-
*
|
|
912
|
-
* @category Composition
|
|
913
|
-
*/
|
|
914
|
-
type MotifCell = {
|
|
915
|
-
notes: MotifNote[];
|
|
916
|
-
};
|
|
917
|
-
/**
|
|
918
|
-
* Melodic contour shape for {@link generateMotif}.
|
|
919
|
-
*
|
|
920
|
-
* @category Composition
|
|
921
|
-
*/
|
|
922
|
-
type MotifContour = 'arch' | 'ascending' | 'descending' | 'wave';
|
|
923
|
-
/**
|
|
924
|
-
* Options controlling {@link generateMotif}.
|
|
925
|
-
*
|
|
926
|
-
* @category Composition
|
|
927
|
-
*/
|
|
928
|
-
type MotifOptions = {
|
|
929
|
-
key: KeyScale;
|
|
930
|
-
chord?: Chord | null;
|
|
931
|
-
/** Length of the motif in bars of `ts`. */
|
|
932
|
-
bars: number;
|
|
933
|
-
/**
|
|
934
|
-
* Meter the bars are counted in. It sets the bar length, so a motif shares a
|
|
935
|
-
* bar grid with the other generators instead of assuming four beats.
|
|
936
|
-
*
|
|
937
|
-
* @defaultValue 4/4
|
|
938
|
-
*/
|
|
939
|
-
ts?: TimeSignature;
|
|
536
|
+
rate?: 'straight' | 'half' | 'double';
|
|
940
537
|
/**
|
|
941
|
-
*
|
|
538
|
+
* The swing the figure is played with. Some entries are written on the
|
|
539
|
+
* straight grid and are only themselves once a triplet feel is applied at
|
|
540
|
+
* render — the half-time shuffle is the plain case — so this is the surface
|
|
541
|
+
* those entries name when they say the feel comes from the performance.
|
|
942
542
|
*
|
|
943
|
-
* @defaultValue `'
|
|
543
|
+
* @defaultValue `'straight'`
|
|
944
544
|
*/
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
|
|
948
|
-
* nudge direction is balanced (up or down with equal odds), so it adds
|
|
949
|
-
* variety without biasing the line off its contour. Default 0, which
|
|
950
|
-
* reproduces the requested contour exactly (no drift, tails return to tonic).
|
|
951
|
-
*
|
|
952
|
-
* @defaultValue 0
|
|
953
|
-
*/
|
|
954
|
-
jitter?: number;
|
|
545
|
+
feel?: GrooveFeel;
|
|
546
|
+
/** Base velocity the figure's own factors are read against. */
|
|
547
|
+
velocity?: number;
|
|
955
548
|
/**
|
|
956
|
-
*
|
|
549
|
+
* Maximum number of onsets {@link placeDrumPattern} may write. One figure is
|
|
550
|
+
* placed per bar, so this is the guard against an unbounded caller rather
|
|
551
|
+
* than a limit on any search.
|
|
957
552
|
*
|
|
958
|
-
* @defaultValue
|
|
553
|
+
* @defaultValue 1000000
|
|
959
554
|
*/
|
|
960
|
-
|
|
555
|
+
budget?: number;
|
|
961
556
|
};
|
|
962
557
|
/**
|
|
963
|
-
* Generate a
|
|
964
|
-
*
|
|
965
|
-
* The melody follows the requested contour in diatonic steps from the tonic,
|
|
966
|
-
* snapped to the key. When a chord is supplied, notes landing on bar downbeats
|
|
967
|
-
* are pulled to the nearest chord tone. An optional `jitter` adds balanced
|
|
968
|
-
* per-note variation without biasing the contour; it is off by default, so the
|
|
969
|
-
* contour is reproduced exactly. Output is deterministic for a given seed.
|
|
970
|
-
*
|
|
971
|
-
* @param opts Key, optional chord, length, contour, jitter, and seed.
|
|
972
|
-
* @returns The generated motif cell.
|
|
973
|
-
*
|
|
974
|
-
* @example
|
|
975
|
-
* ```ts
|
|
976
|
-
* import { generateMotif, majorKey } from '@libraz/libcantus';
|
|
977
|
-
* const cell = generateMotif({ key: majorKey(0), bars: 2, contour: 'arch' });
|
|
978
|
-
* // cell.notes is a deterministic MotifCell (seed defaults to 0)
|
|
979
|
-
* ```
|
|
980
|
-
*
|
|
981
|
-
* @category Composition
|
|
982
|
-
*/
|
|
983
|
-
declare function generateMotif(opts: MotifOptions): MotifCell;
|
|
984
|
-
/**
|
|
985
|
-
* Turn a motif cell into note events.
|
|
986
|
-
*
|
|
987
|
-
* A {@link MotifNote} already carries the {@link NoteEvent} fields, but a cell
|
|
988
|
-
* wraps them in an object; this unwraps it, so a motif can be humanized,
|
|
989
|
-
* grooved, or written out like any other line.
|
|
990
|
-
*
|
|
991
|
-
* @param cell The motif to convert.
|
|
992
|
-
* @returns The cell's notes, copied, in the order they are stored.
|
|
993
|
-
* @example
|
|
994
|
-
* ```ts
|
|
995
|
-
* import { generateMotif, humanize, majorKey, motifToNoteEvents } from '@libraz/libcantus';
|
|
996
|
-
* humanize(motifToNoteEvents(generateMotif({ key: majorKey(0), bars: 2 })));
|
|
997
|
-
* ```
|
|
998
|
-
* @category Composition
|
|
999
|
-
*/
|
|
1000
|
-
declare function motifToNoteEvents(cell: MotifCell): NoteEvent[];
|
|
1001
|
-
/**
|
|
1002
|
-
* Apply a transformation to a motif cell.
|
|
558
|
+
* Generate a genre-characteristic groove from the pattern dictionary.
|
|
1003
559
|
*
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1010
|
-
* (by `amount` diatonic degrees with a `key`, or by `amount` semitones without
|
|
1011
|
-
* one, since it delegates to `transposeDiatonic`).
|
|
560
|
+
* The three dials never fight, because each owns a different step: the genre
|
|
561
|
+
* chooses which figures are candidates, the complexity dials deform the chosen
|
|
562
|
+
* figure, and the difficulty ceiling rejects a figure whose closest pair of
|
|
563
|
+
* strokes is faster than a player at that ceiling sustains. A rejected figure
|
|
564
|
+
* is dropped from the candidates rather than simplified, since a simplified
|
|
565
|
+
* figure is a different figure.
|
|
1012
566
|
*
|
|
1013
|
-
* @param
|
|
1014
|
-
* @
|
|
1015
|
-
* @
|
|
1016
|
-
*
|
|
1017
|
-
* `transposeDiatonic` and `sequence` shift chromatically by semitones.
|
|
1018
|
-
* @returns The transformed cell.
|
|
1019
|
-
* @throws If a transformed pitch would fall outside the MIDI range 0..127.
|
|
1020
|
-
*
|
|
1021
|
-
* @example
|
|
1022
|
-
* ```ts
|
|
1023
|
-
* import { generateMotif, transformMotif, majorKey } from '@libraz/libcantus';
|
|
1024
|
-
* const cell = generateMotif({ key: majorKey(0), bars: 1 });
|
|
1025
|
-
* const inverted = transformMotif(cell, 'invert');
|
|
1026
|
-
* ```
|
|
1027
|
-
*
|
|
1028
|
-
* @category Composition
|
|
1029
|
-
*/
|
|
1030
|
-
declare function transformMotif(cell: MotifCell, t: MotifTransform, amount?: number, key?: KeyScale): MotifCell;
|
|
1031
|
-
/**
|
|
1032
|
-
* Lay a motif across `bars` and snap it to a chord timeline.
|
|
1033
|
-
*
|
|
1034
|
-
* The cell is tiled back-to-back to fill the requested span; each note is then
|
|
1035
|
-
* pulled to the nearest chord tone of the segment sounding at its onset, so the
|
|
1036
|
-
* developed line spells the underlying harmony.
|
|
1037
|
-
*
|
|
1038
|
-
* @param cell The source motif.
|
|
1039
|
-
* @param timeline Chord segments to snap against.
|
|
1040
|
-
* @param key Key context (used to keep snapped pitches sensible).
|
|
1041
|
-
* @param bars Number of bars to fill.
|
|
1042
|
-
* @param ts Meter the bars are counted in; defaults to 4/4.
|
|
1043
|
-
* @returns The developed, harmony-aware cell.
|
|
567
|
+
* @param opts Generation options.
|
|
568
|
+
* @returns Percussion onsets in onset order, ties broken by pitch.
|
|
569
|
+
* @throws If the bar count, tempo, or time signature is invalid, or the genre
|
|
570
|
+
* is not one this library names.
|
|
1044
571
|
*
|
|
1045
572
|
* @example
|
|
1046
573
|
* ```ts
|
|
1047
|
-
* import {
|
|
1048
|
-
* const
|
|
1049
|
-
* const timeline = chordTimelineFromChords([{ rootPc: 0, quality: 'maj', startBeat: 0 }], 8);
|
|
1050
|
-
* const developed = developMotif(generateMotif({ key, bars: 1 }), timeline, key, 2);
|
|
574
|
+
* import { placeDrumPattern } from '@libraz/libcantus';
|
|
575
|
+
* const hits = placeDrumPattern({ bars: 2, genre: 'bossa', ctx: { seed: 7, bpm: 130 } });
|
|
1051
576
|
* ```
|
|
1052
577
|
*
|
|
1053
578
|
* @category Composition
|
|
1054
579
|
*/
|
|
1055
|
-
declare function
|
|
580
|
+
declare function placeDrumPattern(opts: DrumPatternOptions): DrumHit[];
|
|
1056
581
|
|
|
1057
582
|
/**
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
* alternative harmonies that preserve a chosen relationship — a shared function,
|
|
1061
|
-
* a common tone, a tonicizing dominant, or a reflection across the key axis.
|
|
1062
|
-
*
|
|
1063
|
-
* Roots are pitch classes (0..11); no spelled key signature is required.
|
|
1064
|
-
*/
|
|
1065
|
-
|
|
1066
|
-
/**
|
|
1067
|
-
* The kind of substitution relationship a candidate realizes.
|
|
1068
|
-
*
|
|
1069
|
-
* @category Reharmonization
|
|
1070
|
-
*/
|
|
1071
|
-
type SubstitutionType = 'tritone' | 'relative' | 'borrowed' | 'chromaticMediant' | 'secondaryDominant';
|
|
1072
|
-
/**
|
|
1073
|
-
* A proposed chord substitution with its relationship, numeral, and function.
|
|
583
|
+
* What a melody note contributes to the harmony: either a structural tone the
|
|
584
|
+
* chord has to account for, or the ornament figure that explains it away.
|
|
1074
585
|
*
|
|
1075
586
|
* @category Reharmonization
|
|
1076
587
|
*/
|
|
1077
|
-
type
|
|
1078
|
-
chord: Chord;
|
|
1079
|
-
type: SubstitutionType;
|
|
1080
|
-
roman: string;
|
|
1081
|
-
function: HarmonicFunction;
|
|
1082
|
-
};
|
|
588
|
+
type MelodyToneRole = 'structural' | 'passing' | 'neighbor' | 'appoggiatura' | 'suspension' | 'anticipation' | 'escape';
|
|
1083
589
|
/**
|
|
1084
|
-
*
|
|
590
|
+
* One melody note's classification: its index in the classified melody, the
|
|
591
|
+
* figure it forms, and whether that figure makes it ornamental.
|
|
1085
592
|
*
|
|
1086
593
|
* @category Reharmonization
|
|
1087
594
|
*/
|
|
1088
|
-
type
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
*/
|
|
1094
|
-
melodyPcs?: number[];
|
|
595
|
+
type ClassifiedMelodyTone = {
|
|
596
|
+
noteIndex: number;
|
|
597
|
+
role: MelodyToneRole;
|
|
598
|
+
/** True for every role but `'structural'`. */
|
|
599
|
+
ornamental: boolean;
|
|
1095
600
|
};
|
|
1096
601
|
/**
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
* @param
|
|
1115
|
-
* @
|
|
1116
|
-
* @param opts Optional melody-preservation constraint.
|
|
1117
|
-
* @returns The deduplicated substitution candidates.
|
|
602
|
+
* Classify a melody's ornaments from the melody and the metre alone.
|
|
603
|
+
*
|
|
604
|
+
* This is deliberately chord-free: a harmonizer needs to know which notes the
|
|
605
|
+
* chords must explain *before* it can choose them, so nothing here may consult a
|
|
606
|
+
* harmony. What remains is the melodic and metric evidence — stepwise approach
|
|
607
|
+
* and departure, repeated pitches, leaps, and the relative metric weight and
|
|
608
|
+
* length of a note against its neighbours.
|
|
609
|
+
*
|
|
610
|
+
* The figures are recognized in the order suspension, passing, neighbor,
|
|
611
|
+
* anticipation, appoggiatura, escape. A note is judged ornamental only when it
|
|
612
|
+
* is metrically and rhythmically subordinate to the neighbours that frame it
|
|
613
|
+
* (or, for the accented figures, when it leans on a stronger beat than its
|
|
614
|
+
* approach and gives way to a resolution). A note without an adjacent
|
|
615
|
+
* predecessor and successor has no figure to belong to and stays structural, so
|
|
616
|
+
* the first and last notes of a phrase are always structural.
|
|
617
|
+
*
|
|
618
|
+
* @param melody The melody, in ascending onset order.
|
|
619
|
+
* @param ts Time signature used to weight metric accents.
|
|
620
|
+
* @returns One classification per input note, in input order.
|
|
1118
621
|
* @example
|
|
1119
622
|
* ```ts
|
|
1120
|
-
* import {
|
|
1121
|
-
* const
|
|
1122
|
-
*
|
|
623
|
+
* import { classifyMelodyTones } from '@libraz/libcantus';
|
|
624
|
+
* const melody = [60, 62, 64, 60].map((pitch, i) => ({
|
|
625
|
+
* pitch,
|
|
626
|
+
* startBeat: i,
|
|
627
|
+
* durationBeat: 1,
|
|
628
|
+
* }));
|
|
629
|
+
* classifyMelodyTones(melody, { numerator: 4, denominator: 4 })[1];
|
|
630
|
+
* // { noteIndex: 1, role: 'passing', ornamental: true }
|
|
1123
631
|
* ```
|
|
1124
632
|
* @category Reharmonization
|
|
1125
633
|
*/
|
|
1126
|
-
declare function
|
|
1127
|
-
/**
|
|
1128
|
-
* A borrowed chord in the modal-interchange palette of a key.
|
|
1129
|
-
*
|
|
1130
|
-
* @category Reharmonization
|
|
1131
|
-
*/
|
|
1132
|
-
type BorrowedChord = {
|
|
1133
|
-
chord: Chord;
|
|
1134
|
-
roman: string;
|
|
1135
|
-
source: BorrowedSource;
|
|
1136
|
-
};
|
|
1137
|
-
/**
|
|
1138
|
-
* The modal-interchange palette of a key.
|
|
1139
|
-
*
|
|
1140
|
-
* Lists the diatonic triads of the {@link parallelKey} that are not diatonic to
|
|
1141
|
-
* `key` (the classic borrowed chords such as `iv`, `bVI`, and `bVII` in a major
|
|
1142
|
-
* key), plus the Neapolitan (a major triad on the flat second degree). Each
|
|
1143
|
-
* carries its Roman numeral and {@link borrowedSource} relative to `key`.
|
|
1144
|
-
*
|
|
1145
|
-
* @param key The prevailing key.
|
|
1146
|
-
* @returns The borrowed-chord palette.
|
|
1147
|
-
* @category Reharmonization
|
|
1148
|
-
*/
|
|
1149
|
-
declare function modalInterchangePalette(key: KeyScale): BorrowedChord[];
|
|
1150
|
-
/**
|
|
1151
|
-
* Reflect a chord across the tonic–dominant axis of a key (negative harmony).
|
|
1152
|
-
*
|
|
1153
|
-
* Every pitch class `p` maps to `(2*T + 7 - p) mod 12`, where `T` is the key
|
|
1154
|
-
* tonic — the reflection that swaps the tonic with the dominant. The mirrored
|
|
1155
|
-
* pitch classes (including the bass, if set) are reassembled into a chord by
|
|
1156
|
-
* {@link detectChordBest}; when no chord is recognized, the mirrored root and
|
|
1157
|
-
* the original quality are used as a fallback.
|
|
1158
|
-
*
|
|
1159
|
-
* @param chord The chord to reflect.
|
|
1160
|
-
* @param key The prevailing key.
|
|
1161
|
-
* @returns The negative-harmony counterpart of the chord.
|
|
1162
|
-
* @example
|
|
1163
|
-
* ```ts
|
|
1164
|
-
* import { negativeHarmonyMirror, makeChord, majorKey } from '@libraz/libcantus';
|
|
1165
|
-
* negativeHarmonyMirror(makeChord(7, 'maj'), majorKey(0));
|
|
1166
|
-
* // G major reflected across C's axis => F minor (rootPc 5, quality 'min')
|
|
1167
|
-
* ```
|
|
1168
|
-
* @category Reharmonization
|
|
1169
|
-
*/
|
|
1170
|
-
declare function negativeHarmonyMirror(chord: Chord, key: KeyScale): Chord;
|
|
1171
|
-
|
|
1172
|
-
/**
|
|
1173
|
-
* Deterministic rhythm generation over a metric grid.
|
|
1174
|
-
*
|
|
1175
|
-
* Builds a fixed-resolution grid across one or more bars of a time signature
|
|
1176
|
-
* and decides, per grid slot, whether it becomes a note onset. Onset
|
|
1177
|
-
* probability follows the metric-accent hierarchy from the `meter` module, so
|
|
1178
|
-
* strong positions (downbeats, secondary pulses) are favored over weak
|
|
1179
|
-
* off-pulse subdivisions. Given a seed the output is fully reproducible.
|
|
1180
|
-
*
|
|
1181
|
-
* Positions and durations are measured in quarter-note beats, matching the
|
|
1182
|
-
* library-wide convention.
|
|
1183
|
-
*/
|
|
1184
|
-
|
|
1185
|
-
/**
|
|
1186
|
-
* A single rhythmic note: an onset position and how long it sounds.
|
|
1187
|
-
*
|
|
1188
|
-
* @category Rhythm & Meter
|
|
1189
|
-
*/
|
|
1190
|
-
type RhythmEvent = {
|
|
1191
|
-
/** Onset position in quarter-note beats, absolute from the start of the span. */
|
|
1192
|
-
position: number;
|
|
1193
|
-
/** Duration in quarter-note beats, extending to the next onset. */
|
|
1194
|
-
duration: number;
|
|
1195
|
-
};
|
|
1196
|
-
/**
|
|
1197
|
-
* Turn rhythm events into note events at a fixed pitch.
|
|
1198
|
-
*
|
|
1199
|
-
* {@link RhythmEvent} names its fields `position`/`duration` because a rhythm
|
|
1200
|
-
* has no pitch; every other generator in the library speaks {@link NoteEvent}.
|
|
1201
|
-
* This is the bridge, so a generated rhythm can be humanized, grooved, or
|
|
1202
|
-
* written out like any other line.
|
|
1203
|
-
*
|
|
1204
|
-
* @param events The rhythm events to convert.
|
|
1205
|
-
* @param pitch The MIDI pitch to give every note.
|
|
1206
|
-
* @param velocity Velocity for every note; defaults to a mezzo-forte 96.
|
|
1207
|
-
* @returns The note events, in the order given.
|
|
1208
|
-
* @example
|
|
1209
|
-
* ```ts
|
|
1210
|
-
* import { generateRhythm, humanize, rhythmToNoteEvents } from '@libraz/libcantus';
|
|
1211
|
-
* const rhythm = generateRhythm({ numerator: 4, denominator: 4 }, { bars: 2 });
|
|
1212
|
-
* humanize(rhythmToNoteEvents(rhythm, 38));
|
|
1213
|
-
* ```
|
|
1214
|
-
* @category Rhythm & Meter
|
|
1215
|
-
*/
|
|
1216
|
-
declare function rhythmToNoteEvents(events: RhythmEvent[], pitch: number, velocity?: number): NoteEvent[];
|
|
1217
|
-
/**
|
|
1218
|
-
* Options controlling {@link generateRhythm}.
|
|
1219
|
-
*
|
|
1220
|
-
* @category Rhythm & Meter
|
|
1221
|
-
*/
|
|
1222
|
-
type RhythmOptions = {
|
|
1223
|
-
/**
|
|
1224
|
-
* Seed for the deterministic PRNG.
|
|
1225
|
-
*
|
|
1226
|
-
* @defaultValue 0
|
|
1227
|
-
*/
|
|
1228
|
-
seed?: number;
|
|
1229
|
-
/**
|
|
1230
|
-
* Number of bars to generate.
|
|
1231
|
-
*
|
|
1232
|
-
* @defaultValue 1
|
|
1233
|
-
*/
|
|
1234
|
-
bars?: number;
|
|
1235
|
-
/**
|
|
1236
|
-
* Grid resolution as the number of equal grid steps per quarter-note beat.
|
|
1237
|
-
* The default of 2 yields an eighth-note grid; 4 yields a sixteenth-note
|
|
1238
|
-
* grid, 3 an eighth-note triplet grid, and so on.
|
|
1239
|
-
*
|
|
1240
|
-
* @defaultValue 2
|
|
1241
|
-
*/
|
|
1242
|
-
subdivision?: number;
|
|
1243
|
-
/**
|
|
1244
|
-
* Overall onset density in [0, 1]. Scales the per-slot onset probability, so
|
|
1245
|
-
* higher values fill more grid slots. Values outside [0, 1] are clamped into
|
|
1246
|
-
* range.
|
|
1247
|
-
*
|
|
1248
|
-
* @defaultValue 0.5
|
|
1249
|
-
*/
|
|
1250
|
-
density?: number;
|
|
1251
|
-
};
|
|
1252
|
-
/**
|
|
1253
|
-
* Map a metric weight (0–3 from {@link metricWeight}) to a base onset
|
|
1254
|
-
* probability before density scaling. Strong positions are much more likely to
|
|
1255
|
-
* carry an onset than weak ones; the curve is intentionally small and explicit
|
|
1256
|
-
* so its behavior is easy to inspect and test.
|
|
1257
|
-
*
|
|
1258
|
-
* @param weight The metric weight, 0 (off-pulse) to 3 (downbeat).
|
|
1259
|
-
* @returns A base onset probability in [0, 1].
|
|
1260
|
-
* @category Rhythm & Meter
|
|
1261
|
-
*/
|
|
1262
|
-
declare function onsetWeightCurve(weight: number): number;
|
|
1263
|
-
/**
|
|
1264
|
-
* Generate a deterministic rhythmic pattern over `bars` bars of a time
|
|
1265
|
-
* signature. A grid is built at the chosen subdivision; each slot becomes an
|
|
1266
|
-
* onset with a probability derived from its metric weight scaled by `density`
|
|
1267
|
-
* and sampled from the seeded PRNG. Every bar's downbeat is always an onset, so
|
|
1268
|
-
* a pattern of any length has a reliable pulse independent of the seed. Each
|
|
1269
|
-
* event's duration extends to the next onset, and the last event extends to the
|
|
1270
|
-
* end of the span. `density` is clamped to [0, 1].
|
|
1271
|
-
*
|
|
1272
|
-
* @param ts The time signature.
|
|
1273
|
-
* @param opts Generation options.
|
|
1274
|
-
* @returns Onset events sorted by position, non-overlapping, covering the span.
|
|
1275
|
-
* @example
|
|
1276
|
-
* ```ts
|
|
1277
|
-
* import { parseTimeSignature, generateRhythm } from '@libraz/libcantus';
|
|
1278
|
-
* const ts = parseTimeSignature('4/4');
|
|
1279
|
-
* generateRhythm(ts, { seed: 42, density: 0.6 }); // onset events over one bar
|
|
1280
|
-
* ```
|
|
1281
|
-
* @category Rhythm & Meter
|
|
1282
|
-
*/
|
|
1283
|
-
declare function generateRhythm(ts: TimeSignature, opts?: RhythmOptions): RhythmEvent[];
|
|
1284
|
-
/**
|
|
1285
|
-
* Onset density of a generated pattern: the mean number of onsets per bar.
|
|
1286
|
-
*
|
|
1287
|
-
* @param events Events from {@link generateRhythm}.
|
|
1288
|
-
* @param ts The time signature.
|
|
1289
|
-
* @returns The onset count divided by the number of bars the events span.
|
|
1290
|
-
* @category Rhythm & Meter
|
|
1291
|
-
*/
|
|
1292
|
-
declare function rhythmDensity(events: RhythmEvent[], ts: TimeSignature): number;
|
|
634
|
+
declare function classifyMelodyTones(melody: readonly NoteEvent[], ts: TimeSignature): ClassifiedMelodyTone[];
|
|
1293
635
|
|
|
1294
|
-
export {
|
|
636
|
+
export { Articulation, BASS_LICKS, type BassLick, ChordLike, ChordSegment, type ClassifiedMelodyTone, DRUM_KIT, DRUM_PATTERNS, DrumHit, type DrumPattern, type DrumPatternOptions, type DrumStroke, DrumStyle, type DrumVocabulary, DrumVoice, FILL_ARCHETYPES, FILL_TYPES, type FillArchetype, type FillStroke, type FillType, type FillVelocity, GenerationContextInput, Genre, GridEvent, GrooveFeel, type ImitationAnswer, type ImitationOptions, IntervalLike, KICK_FIGURES, KICK_STEPS, KeyLike, type KickFigure, type KickPattern, type KickSlot, type LickMaterial, type LickNote, Limb, MAX_DIFFICULTY, MIN_DIFFICULTY, type MelodyToneRole, NoteEvent, PercussionProfile, type PlaceLicksOptions, Section, StringedProfile, TimeSignature, Vocabulary, classifyMelodyTones, imitate, isDrumPattern, isFillArchetype, isLickMaterial, placeDrumPattern, placeLicks, sustainsShift, sustainsStrokes };
|