@libraz/libcantus 1.0.0 → 1.1.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.
Files changed (142) hide show
  1. package/README.md +27 -65
  2. package/README_ja.md +28 -66
  3. package/dist/analyze/index.cjs +11 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +79 -144
  6. package/dist/analyze/index.d.ts +79 -144
  7. package/dist/analyze/index.js +20 -16
  8. package/dist/{chunk-TTCLJX6D.js → chunk-57WB2EDF.js} +10 -8
  9. package/dist/chunk-57WB2EDF.js.map +1 -0
  10. package/dist/chunk-5JJ6EHEZ.cjs +457 -0
  11. package/dist/chunk-5JJ6EHEZ.cjs.map +1 -0
  12. package/dist/{chunk-AFM2ULJH.cjs → chunk-5KW7DNCJ.cjs} +1567 -1067
  13. package/dist/chunk-5KW7DNCJ.cjs.map +1 -0
  14. package/dist/chunk-7GOKQJTQ.js +145 -0
  15. package/dist/chunk-7GOKQJTQ.js.map +1 -0
  16. package/dist/{chunk-H3VORETI.js → chunk-AWEMD747.js} +487 -592
  17. package/dist/chunk-AWEMD747.js.map +1 -0
  18. package/dist/{chunk-4A2RMUCC.js → chunk-DKICR7AN.js} +1216 -611
  19. package/dist/chunk-DKICR7AN.js.map +1 -0
  20. package/dist/{chunk-INHVXP6Z.js → chunk-FPBZFNCD.js} +1159 -415
  21. package/dist/chunk-FPBZFNCD.js.map +1 -0
  22. package/dist/{chunk-M4CWTJWJ.cjs → chunk-GDKE6RQW.cjs} +1453 -848
  23. package/dist/chunk-GDKE6RQW.cjs.map +1 -0
  24. package/dist/chunk-H66ARFU5.js +363 -0
  25. package/dist/chunk-H66ARFU5.js.map +1 -0
  26. package/dist/{chunk-YH3BIKHH.js → chunk-HOHFYGKW.js} +146 -49
  27. package/dist/chunk-HOHFYGKW.js.map +1 -0
  28. package/dist/chunk-HSY2VQXZ.js +2272 -0
  29. package/dist/chunk-HSY2VQXZ.js.map +1 -0
  30. package/dist/{chunk-E3F4AHAH.cjs → chunk-J772DL3B.cjs} +221 -134
  31. package/dist/chunk-J772DL3B.cjs.map +1 -0
  32. package/dist/chunk-K6OZXIR6.js +1 -0
  33. package/dist/{chunk-VTI6FOLB.js → chunk-PQL7E5F6.js} +709 -133
  34. package/dist/chunk-PQL7E5F6.js.map +1 -0
  35. package/dist/chunk-Q6R3QAZD.cjs +3329 -0
  36. package/dist/chunk-Q6R3QAZD.cjs.map +1 -0
  37. package/dist/{chunk-MXO72PLY.cjs → chunk-QCXYKSZS.cjs} +21 -19
  38. package/dist/chunk-QCXYKSZS.cjs.map +1 -0
  39. package/dist/{chunk-VOHZB45S.js → chunk-R5VUXQMP.js} +1321 -821
  40. package/dist/chunk-R5VUXQMP.js.map +1 -0
  41. package/dist/chunk-R7B5N2JK.cjs +363 -0
  42. package/dist/chunk-R7B5N2JK.cjs.map +1 -0
  43. package/dist/{chunk-WWURE4XT.cjs → chunk-RVS4G4MR.cjs} +624 -729
  44. package/dist/chunk-RVS4G4MR.cjs.map +1 -0
  45. package/dist/chunk-TT6PKMPR.cjs +145 -0
  46. package/dist/chunk-TT6PKMPR.cjs.map +1 -0
  47. package/dist/chunk-UGLFDG7L.cjs +1 -0
  48. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  49. package/dist/chunk-VLE5R7PF.js +3329 -0
  50. package/dist/chunk-VLE5R7PF.js.map +1 -0
  51. package/dist/{chunk-FVSI36UV.js → chunk-X6RJAHII.js} +197 -110
  52. package/dist/chunk-X6RJAHII.js.map +1 -0
  53. package/dist/{chunk-Q2WGKJNN.cjs → chunk-XKSO2V5I.cjs} +699 -123
  54. package/dist/chunk-XKSO2V5I.cjs.map +1 -0
  55. package/dist/chunk-XVMJO3QF.cjs +2272 -0
  56. package/dist/chunk-XVMJO3QF.cjs.map +1 -0
  57. package/dist/{chunk-UADYAYAT.cjs → chunk-YPB3A3EY.cjs} +1309 -565
  58. package/dist/chunk-YPB3A3EY.cjs.map +1 -0
  59. package/dist/{index-C_8o--VE.d.cts → coerce-CJlgULrQ.d.cts} +210 -199
  60. package/dist/{index-szBv3496.d.ts → coerce-wwdELNYS.d.ts} +210 -199
  61. package/dist/core/index.cjs +9 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +122 -108
  64. package/dist/core/index.d.ts +122 -108
  65. package/dist/core/index.js +32 -28
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.d.cts +168 -84
  68. package/dist/generate/index.d.ts +168 -84
  69. package/dist/generate/index.js +7 -7
  70. package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
  71. package/dist/{index-Dzr13q3d.d.cts → index-B3WCj_Hm.d.cts} +135 -58
  72. package/dist/{index-BO6xE9YU.d.cts → index-BUtIf2go.d.ts} +460 -254
  73. package/dist/{index-CCNJRG-P.d.cts → index-BaoKVVhE.d.cts} +293 -56
  74. package/dist/{index-Chbst3Jx.d.ts → index-BpWOhkkE.d.ts} +135 -58
  75. package/dist/{index-BGFW-8P2.d.cts → index-BzLNkmA7.d.cts} +196 -114
  76. package/dist/{index-CunYKLiu.d.ts → index-CBCP84rv.d.cts} +1 -1
  77. package/dist/{index-BkGmlyI3.d.cts → index-Ci3xyows.d.ts} +60 -23
  78. package/dist/{index-9wcMhG73.d.ts → index-CnHQgqNh.d.ts} +293 -56
  79. package/dist/{index-B4o55NDr.d.cts → index-CnIdP4EZ.d.ts} +1 -1
  80. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  81. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  82. package/dist/{index-D57QRbVn.d.ts → index-DTsKaPaC.d.cts} +460 -254
  83. package/dist/{index-Cj49F892.d.ts → index-D_hzxD8w.d.cts} +60 -23
  84. package/dist/{index-CgHqxdps.d.ts → index-DpC-mDlV.d.ts} +196 -114
  85. package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
  86. package/dist/index.cjs +42 -14
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +14 -14
  89. package/dist/index.d.ts +14 -14
  90. package/dist/index.js +82 -54
  91. package/dist/{masks-DYbE485J.d.ts → masks-C8TWLFIi.d.cts} +49 -2
  92. package/dist/{masks-DYbE485J.d.cts → masks-O73Rhj5d.d.ts} +49 -2
  93. package/dist/model/index.cjs +19 -12
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +502 -181
  96. package/dist/model/index.d.ts +502 -181
  97. package/dist/model/index.js +18 -11
  98. package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
  99. package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
  100. package/dist/theory/index.cjs +35 -6
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +257 -62
  103. package/dist/theory/index.d.ts +257 -62
  104. package/dist/theory/index.js +41 -12
  105. package/package.json +6 -5
  106. package/dist/chunk-3JXCW6DX.cjs +0 -3220
  107. package/dist/chunk-3JXCW6DX.cjs.map +0 -1
  108. package/dist/chunk-4A2RMUCC.js.map +0 -1
  109. package/dist/chunk-6XPTBE7Y.cjs +0 -49
  110. package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
  111. package/dist/chunk-AFM2ULJH.cjs.map +0 -1
  112. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  113. package/dist/chunk-ELCTZUJJ.js +0 -379
  114. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  115. package/dist/chunk-FVSI36UV.js.map +0 -1
  116. package/dist/chunk-GOZG57ER.js +0 -3220
  117. package/dist/chunk-GOZG57ER.js.map +0 -1
  118. package/dist/chunk-H3VORETI.js.map +0 -1
  119. package/dist/chunk-H42UHPYW.js +0 -1577
  120. package/dist/chunk-H42UHPYW.js.map +0 -1
  121. package/dist/chunk-HFPS7JFD.js +0 -1
  122. package/dist/chunk-INHVXP6Z.js.map +0 -1
  123. package/dist/chunk-K4ZJUGF4.cjs +0 -379
  124. package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
  125. package/dist/chunk-KNJEKLKV.js +0 -49
  126. package/dist/chunk-KNJEKLKV.js.map +0 -1
  127. package/dist/chunk-M4CWTJWJ.cjs.map +0 -1
  128. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  129. package/dist/chunk-NLEU4QSE.cjs +0 -360
  130. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  131. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  132. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  133. package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
  134. package/dist/chunk-RHIJ3S4I.cjs +0 -1
  135. package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
  136. package/dist/chunk-TTCLJX6D.js.map +0 -1
  137. package/dist/chunk-UADYAYAT.cjs.map +0 -1
  138. package/dist/chunk-VOHZB45S.js.map +0 -1
  139. package/dist/chunk-VTI6FOLB.js.map +0 -1
  140. package/dist/chunk-WWURE4XT.cjs.map +0 -1
  141. package/dist/chunk-YH3BIKHH.js.map +0 -1
  142. /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
@@ -1,14 +1,15 @@
1
- import { a as Articulation, g as NoteEvent, I as IntervalLike } from '../index-B9MNZboQ.js';
2
- export { b as IntervalQualityLabel, d as KeyScale, S as SpelledInterval } from '../index-B9MNZboQ.js';
3
- import { e as ChordSegment, c as ChordLike, K as KeyLike } from '../index-szBv3496.js';
4
- export { b as ChordData, d as ChordQuality, g as ChordSpan, j as ChordToneRole, l as PitchSpelling, m as ProfileWeights } from '../index-szBv3496.js';
5
- export { B as BorrowedSource, e as ChordTimeline, H as HarmonicFunction } from '../index-Chbst3Jx.js';
6
- import { f as StringedProfile, P as PercussionProfile, c as Limb } from '../positional-Cptlj-xs.js';
7
- export { I as InstrumentProfile, a as InstrumentProfileCommon, d as PositionalRng, S as SeedPath } from '../positional-Cptlj-xs.js';
8
- import { T as TimeSignature } from '../index-Cj49F892.js';
9
- export { H as HarmonyRole } from '../index-CunYKLiu.js';
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-CgHqxdps.js';
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-CgHqxdps.js';
1
+ import { h as NoteEvent, a as Articulation, b as IntervalLike } from '../index-Cy15oTvh.js';
2
+ export { I as IntervalData, c as IntervalQualityLabel, e as KeyScale, g as NoteData, S as SpelledInterval } from '../index-Cy15oTvh.js';
3
+ import { e as ChordSegment, c as ChordLike, K as KeyLike } from '../coerce-wwdELNYS.js';
4
+ export { b as ChordData, d as ChordQuality, g as ChordSpan, i as ChordToneRole, k as KeyVariant, l as PitchSpelling, m as ProfileWeights, n as ResolvedKey, q as SpelledKeyLike, r as SpelledKeyScale } from '../coerce-wwdELNYS.js';
5
+ export { B as BorrowedSource, e as ChordTimeline, H as HarmonicFunction } from '../index-BpWOhkkE.js';
6
+ import { f as StringedProfile, P as PercussionProfile, c as Limb } from '../positional-DJgs6ltD.js';
7
+ export { I as InstrumentProfile, a as InstrumentProfileCommon, d as PositionalRng, S as SeedPath } from '../positional-DJgs6ltD.js';
8
+ import { b as MeterLike } from '../index-Ci3xyows.js';
9
+ export { M as MeterChange, c as MeterMap, T as TimeSignature } from '../index-Ci3xyows.js';
10
+ export { H as HarmonyRole } from '../index-CnIdP4EZ.js';
11
+ import { r as Genre, q as GenerationContextInput, a4 as Vocabulary, s as GridEvent, m as DrumStyle, $ as Section, n as DrumVoice, t as GrooveFeel, k as DrumHit, H as HarmonizeOptions, y as HarmonizeResult } from '../index-DpC-mDlV.js';
12
+ 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, x as HarmonizePlacement, 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, a3 as SyncopateOptions, a5 as VocabularyQuery, a6 as applyGrooveTemplate, a7 as assertVocabulary, a8 as deform, a9 as developMotif, aa as double, ab as doubleTime, ac as drumVoiceOf, ad as extractGrooveTemplate, ae as fitsQuery, af as generateBassLine, ag as generateCounterMelody, ah as generateDrums, ai as generateMotif, aj as generateProgression, ak as generateRhythm, al as gridMetricWeight, am as halfTime, 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-DpC-mDlV.js';
12
13
 
13
14
  /**
14
15
  * The playing-difficulty ceiling, as the generators apply it.
@@ -47,65 +48,15 @@ declare function sustainsStrokes(stepBeats: number, bpm: number | undefined, dif
47
48
  declare function sustainsShift(semitones: number, stepBeats: number, bpm: number | undefined, difficulty: number | undefined): boolean;
48
49
 
49
50
  /**
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.
51
+ * Lay the figures of the dictionary over a chord progression.
53
52
  *
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.
53
+ * A segment takes a figure its genre plays, chosen by a draw addressed to that
54
+ * segment, and the figure's degrees are read against the chord sounding under
55
+ * it. What the figures are is `licks-data.ts`; which pitch a degree asks for is
56
+ * `degrees.ts`. This module is what places them: which segment takes one, how
57
+ * it is fitted to the span, and how one figure leads into the next.
58
58
  */
59
59
 
60
- /**
61
- * One note of a lick, relative to the chord it is played over.
62
- *
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.
68
- *
69
- * @category Composition
70
- */
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;
80
- };
81
- /**
82
- * A lick: its notes and how long the whole figure lasts.
83
- *
84
- * @category Composition
85
- */
86
- type LickMaterial = {
87
- /** Length of the figure in sixteenths. */
88
- lengthSteps: number;
89
- notes: LickNote[];
90
- };
91
- /**
92
- * A lick with the conditions it fits.
93
- *
94
- * @category Composition
95
- */
96
- type BassLick = Vocabulary<LickMaterial>;
97
- /**
98
- * Whether a caller's material is a bass lick.
99
- *
100
- * @category Composition
101
- */
102
- declare function isLickMaterial(material: unknown): material is LickMaterial;
103
- /**
104
- * The built-in bass licks, in declaration order.
105
- *
106
- * @category Composition
107
- */
108
- declare const BASS_LICKS: readonly BassLick[];
109
60
  /**
110
61
  * Options controlling {@link placeLicks}.
111
62
  *
@@ -122,8 +73,8 @@ type PlaceLicksOptions = {
122
73
  * `ctx: { complexity: { difficulty } }`; the context wins over both.
123
74
  */
124
75
  difficulty?: number;
125
- /** Time signature; defaults to 4/4. */
126
- ts?: TimeSignature;
76
+ /** Time signature, in any form that names one; defaults to 4/4. */
77
+ ts?: MeterLike;
127
78
  /**
128
79
  * Target register as a base MIDI octave; roots land around `octave*12+12`.
129
80
  *
@@ -147,9 +98,10 @@ type PlaceLicksOptions = {
147
98
  */
148
99
  ctx?: GenerationContextInput;
149
100
  /**
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.
101
+ * Maximum number of segments {@link placeLicks} may lay figures over, and of
102
+ * bars those segments span. One figure is placed per bar of a segment, so
103
+ * both are counted: this is the guard against an unbounded caller — a segment
104
+ * running to a beat far past the music — rather than a limit on any search.
153
105
  *
154
106
  * @defaultValue 1000000
155
107
  */
@@ -171,8 +123,10 @@ type PlaceLicksOptions = {
171
123
  * key name such as `'C major'` is read as that key.
172
124
  * @param opts Genre, density, ceiling and seed.
173
125
  * @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.
126
+ * @throws If a segment has a non-positive duration, two segments overlap, or
127
+ * the genre is not one this library names. A bass part is monophonic, so
128
+ * overlapping segments are refused here exactly as {@link generateBassLine}
129
+ * refuses them rather than being laid over each other.
176
130
  *
177
131
  * @example
178
132
  * ```ts
@@ -190,6 +144,74 @@ declare function placeLicks(timeline: readonly (Omit<ChordSegment, 'chord'> & {
190
144
  chord: ChordLike;
191
145
  })[], key: KeyLike, opts: PlaceLicksOptions): NoteEvent[];
192
146
 
147
+ /**
148
+ * The bass lick dictionary: the figures a bass player reaches for, held as
149
+ * chord-relative degrees and beat positions and deformed to fit the progression
150
+ * actually in front of them.
151
+ *
152
+ * Provenance: no entry reproduces a bass line from a particular song. Each is a
153
+ * figure that belongs to nobody — the descending soul walk-down, the octave
154
+ * slap, the boogie shuffle — of the kind every player of that music already
155
+ * plays, and each records the ground it qualifies under.
156
+ */
157
+
158
+ /**
159
+ * One note of a lick, relative to the chord it is played over.
160
+ *
161
+ * A degree is 1-based and counts up from the chord's root: 1 is the root, 3 the
162
+ * third, 8 the octave. Degrees the chord itself names — the third, fifth and
163
+ * seventh — are taken from the chord, so the same figure comes out major over a
164
+ * major chord and minor over a minor one. A degree the chord's quality settles
165
+ * without stating it follows the quality: the third of a suspended chord is the
166
+ * tone the suspension put there, and a seventh chord's remaining degrees are
167
+ * its own chord scale's. Only what the harmony leaves open is taken from the
168
+ * key. That is what lets a figure be written once and fit a whole progression.
169
+ *
170
+ * @category Composition
171
+ */
172
+ type LickNote = GridEvent & {
173
+ /** Chord degree, 1-based; 8 is the octave above the root. */
174
+ degree: number;
175
+ /** Chromatic alteration in semitones, for a flattened or raised degree. */
176
+ alter?: number;
177
+ /**
178
+ * Sounding length in sixteenths. Absent, the note sounds to the next onset,
179
+ * as a line written by {@link generateBassLine} does: the built-in figures
180
+ * name no length, so a figure is legato unless it says otherwise.
181
+ */
182
+ lengthSteps?: number;
183
+ /** How the note is played, when it is more than a plain note. */
184
+ articulation?: Articulation;
185
+ };
186
+ /**
187
+ * A lick: its notes and how long the whole figure lasts.
188
+ *
189
+ * @category Composition
190
+ */
191
+ type LickMaterial = {
192
+ /** Length of the figure in sixteenths. */
193
+ lengthSteps: number;
194
+ notes: LickNote[];
195
+ };
196
+ /**
197
+ * A lick with the conditions it fits.
198
+ *
199
+ * @category Composition
200
+ */
201
+ type BassLick = Vocabulary<LickMaterial>;
202
+ /**
203
+ * Whether a caller's material is a bass lick.
204
+ *
205
+ * @category Composition
206
+ */
207
+ declare function isLickMaterial(material: unknown): material is LickMaterial;
208
+ /**
209
+ * The built-in bass licks, in declaration order.
210
+ *
211
+ * @category Composition
212
+ */
213
+ declare const BASS_LICKS: readonly BassLick[];
214
+
193
215
  /**
194
216
  * Imitative entries: a second voice restating what the first has just sung.
195
217
  *
@@ -207,8 +229,10 @@ declare const IMITATION_ANSWERS: readonly ["real", "tonal"];
207
229
  *
208
230
  * A `real` answer transposes literally, keeping every interval of the subject
209
231
  * 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.
232
+ * degrees instead, so the answer stays in the key: every interval keeps the
233
+ * number of degrees it spanned in the subject, and only its quality changes as
234
+ * the key's own pattern of steps and half steps asks — a major third answered by
235
+ * a minor one.
212
236
  *
213
237
  * @category Composition
214
238
  */
@@ -280,7 +304,9 @@ type ImitationOptions = {
280
304
  * @param opts Where the answer enters, at what interval, and in what key.
281
305
  * @returns The imitation as note events sorted by onset; `[]` when the chosen
282
306
  * 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.
307
+ * sound and are not copied, so the answer can be shorter than the span. Every
308
+ * other field of a copied note — its articulation, and its velocity through
309
+ * `velocityScale` — comes across with it.
284
310
  * @throws If the span is reversed, the answer is not a known kind, or a
285
311
  * transposed pitch leaves the MIDI range.
286
312
  * @example
@@ -344,9 +370,14 @@ type FillArchetype = {
344
370
  /** Strokes per beat of the bar, indexed 0..3. */
345
371
  atBeat: readonly (readonly FillStroke[])[];
346
372
  /**
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.
373
+ * Whether the archetype stops where its own strokes stop, rather than holding
374
+ * its last beat over the beats of the bar it does not write.
375
+ *
376
+ * It answers for an archetype shorter than the bar it is placed in, so the
377
+ * built-in archetypes — written beat by beat against the four-beat bar
378
+ * {@link generateDrums} accepts — are unaffected by it; a caller's archetype
379
+ * that writes fewer beats is where it decides anything. Most figures read
380
+ * naturally when held, but a half-time gesture is bar-long and stops there.
350
381
  */
351
382
  boundToBar?: boolean;
352
383
  };
@@ -425,6 +456,11 @@ declare const KICK_FIGURES: Readonly<Record<DrumStyle, KickFigure>>;
425
456
  * hand at the same instant is what makes a pattern unplayable, and a voice no
426
457
  * limb reaches is not on the kit at all.
427
458
  *
459
+ * The auxiliary percussion — hand-claps, tambourine, shaker — is dubbed over
460
+ * the kit on a pass of its own, so it sounds through a backbeat whose snare and
461
+ * hi-hat already take both hands, and the four limbs stay the bound on the
462
+ * groove itself.
463
+ *
428
464
  * @example
429
465
  * ```ts
430
466
  * import { DRUM_KIT, generateDrums, playability } from '@libraz/libcantus';
@@ -511,8 +547,8 @@ type DrumPatternOptions = {
511
547
  genre: Genre;
512
548
  /** Section the pattern plays in, matched against each entry's own sections. */
513
549
  section?: Section;
514
- /** Time signature; defaults to 4/4. */
515
- ts?: TimeSignature;
550
+ /** Time signature, in any form that names one; defaults to 4/4. */
551
+ ts?: MeterLike;
516
552
  /**
517
553
  * The generation context. `bpm` is the tempo, `complexity.rhythmic` thins the
518
554
  * figure below its middle setting and syncopates it above,
@@ -631,6 +667,54 @@ type ClassifiedMelodyTone = {
631
667
  * ```
632
668
  * @category Reharmonization
633
669
  */
634
- declare function classifyMelodyTones(melody: readonly NoteEvent[], ts: TimeSignature): ClassifiedMelodyTone[];
670
+ declare function classifyMelodyTones(melody: readonly NoteEvent[], ts: MeterLike): ClassifiedMelodyTone[];
671
+
672
+ /**
673
+ * Harmonize a melody with a min-cost chord progression.
674
+ *
675
+ * The melody's ornaments are classified first, from the melody and the metre
676
+ * alone (see {@link classifyMelodyTones}); the chords then have to explain the
677
+ * structural tones only, so a passing tone no longer buys itself a chord. The
678
+ * melody is segmented by `harmonicRhythm`; each segment is scored against the
679
+ * candidate chords the harmonic dial opens — the key's own triads, then the
680
+ * secondary dominants, then the parallel mode's chords, one at a time as
681
+ * `ctx.complexity.harmonic` rises from 0 to 1 — by the fit of those structural
682
+ * tones, and a Viterbi search picks the lowest-cost path using a
683
+ * functional-flow transition cost and a cadence bonus at the end of the melody
684
+ * it is given — one cadence, at the close, unless `phraseEnds` names the closes
685
+ * inside a longer line, and then one at each of them as well. Runs of the same
686
+ * chord are reported once, so the chord count follows the harmony rather than
687
+ * the grid.
688
+ *
689
+ * With `placement.transposeSearch` the melody is moved into the key it is
690
+ * harmonized in, and `transposeSemitones` reports how far; with
691
+ * `placement.octaveSearch` it is moved by octaves into a comfortable register,
692
+ * which leaves the key and the chords untouched. Both may be set, and the
693
+ * reported shift is their sum.
694
+ *
695
+ * The `seed` drives a deterministic tie-break only: it perturbs candidates by a
696
+ * magnitude far below any real cost difference (see `TIE_BREAK_JITTER`),
697
+ * so it can decide between chords or paths of otherwise-equal cost but never
698
+ * overrides melody fit or functional flow. For a well-determined melody the
699
+ * result is identical across seeds; the same seed always yields the same result.
700
+ *
701
+ * @param opts The melody and, optionally, the generation context, key, harmonic
702
+ * rhythm, reharmonization strength, placement search, meter, and seed. Only
703
+ * `melody` is required.
704
+ * @returns The chosen transpose, key, chord path, and per-note roles.
705
+ * @example
706
+ * ```ts
707
+ * import { harmonizeMelody } from '@libraz/libcantus';
708
+ * const result = harmonizeMelody({
709
+ * melody: [
710
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
711
+ * { pitch: 64, startBeat: 1, durationBeat: 1 },
712
+ * ],
713
+ * });
714
+ * result.chords; // one ChordSpan per chord change, on the harmonic-rhythm grid
715
+ * ```
716
+ * @category Reharmonization
717
+ */
718
+ declare function harmonizeMelody(opts: HarmonizeOptions): HarmonizeResult;
635
719
 
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 };
720
+ 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, HarmonizeOptions, HarmonizeResult, 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, MeterLike, NoteEvent, PercussionProfile, type PlaceLicksOptions, Section, StringedProfile, Vocabulary, classifyMelodyTones, harmonizeMelody, imitate, isDrumPattern, isFillArchetype, isLickMaterial, placeDrumPattern, placeLicks, sustainsShift, sustainsStrokes };
@@ -66,13 +66,13 @@ import {
66
66
  transformMotif,
67
67
  vocabularyOfKind,
68
68
  withinCeiling
69
- } from "../chunk-VOHZB45S.js";
70
- import "../chunk-H42UHPYW.js";
71
- import "../chunk-FVSI36UV.js";
72
- import "../chunk-ELCTZUJJ.js";
73
- import "../chunk-KNJEKLKV.js";
74
- import "../chunk-INHVXP6Z.js";
75
- import "../chunk-VTI6FOLB.js";
69
+ } from "../chunk-R5VUXQMP.js";
70
+ import "../chunk-VLE5R7PF.js";
71
+ import "../chunk-X6RJAHII.js";
72
+ import "../chunk-7GOKQJTQ.js";
73
+ import "../chunk-H66ARFU5.js";
74
+ import "../chunk-FPBZFNCD.js";
75
+ import "../chunk-PQL7E5F6.js";
76
76
  export {
77
77
  BAR_STEPS,
78
78
  BASS_LICKS,
@@ -1,5 +1,5 @@
1
- import { g as NoteEvent } from './index-B9MNZboQ.cjs';
2
- import { e as StringFingering, c as Limb, I as InstrumentProfile } from './positional-DvH_sd8u.cjs';
1
+ import { h as NoteEvent } from './index-Cy15oTvh.js';
2
+ import { e as StringFingering, c as Limb, b as InstrumentProfileLike } from './positional-DJgs6ltD.js';
3
3
 
4
4
  /**
5
5
  * What makes a passage hard or impossible on an instrument.
@@ -89,6 +89,9 @@ type PlayabilityReport = {
89
89
  * @param profile The instrument to play it on.
90
90
  * @param bpm Tempo in quarter-note beats per minute; enables the third layer.
91
91
  * @returns Difficulty, the issues found, and each note's string/fret or limb.
92
+ * @throws If the notes are not note events, the tempo is not a finite positive
93
+ * number of beats per minute, or the profile describes no instrument — a neck
94
+ * with no strings or a kit no limb reaches, whatever the passage holds.
92
95
  *
93
96
  * @example
94
97
  * ```ts
@@ -98,7 +101,7 @@ type PlayabilityReport = {
98
101
  * ```
99
102
  * @category Core
100
103
  */
101
- declare function playability(notes: readonly NoteEvent[], profile: InstrumentProfile, bpm?: number): PlayabilityReport;
104
+ declare function playability(notes: readonly NoteEvent[], profile: InstrumentProfileLike, bpm?: number): PlayabilityReport;
102
105
 
103
106
  /**
104
107
  * Note values: the written durations a score shows — a base value, its
@@ -176,7 +179,8 @@ type SpelledDuration = {
176
179
  * import { durationToBeats } from '@libraz/libcantus';
177
180
  * durationToBeats('quarter'); // 1
178
181
  * durationToBeats({ base: 'quarter', dots: 1 }); // 1.5
179
- * durationToBeats({ base: 'eighth', tuplet: { actual: 3, normal: 2 } }); // 1/3
182
+ * durationToBeats({ base: 'eighth', tuplet: { actual: 3, normal: 2 } }) * 3; // 1 — three
183
+ * // eighth-note triplets fill the beat two eighths would
180
184
  * ```
181
185
  * @category Rhythm & Meter
182
186
  */
@@ -192,8 +196,10 @@ declare function durationToBeats(duration: NoteValue | DurationData, options?: {
192
196
  * values are preferred to dotted ones and both to tuplets, and within the
193
197
  * tuplets triplets come before quintuplets and septuplets.
194
198
  *
195
- * Lengths are matched within 1e-6 beats, so a rounded decimal such as 0.3333333
196
- * still reads as a triplet.
199
+ * Lengths are matched within 1e-6 quarter notes, so a rounded decimal such as
200
+ * 0.3333333 still reads as a triplet. The tolerance is measured in quarter
201
+ * notes whatever `beatUnit` names, so a longer beat unit narrows it in
202
+ * proportion: under `{ beatUnit: 'whole' }` it is 2.5e-7 of a beat.
197
203
  *
198
204
  * @param beats The length in beats.
199
205
  * @param options `beatUnit` sets what one beat is; a quarter note by default.
@@ -219,9 +225,12 @@ declare function beatsToDuration(beats: number, options?: {
219
225
  * else is broken into undotted values, longest first, and a value immediately
220
226
  * followed by its own half is then written as a dot instead: 5 beats reads as a
221
227
  * whole note tied to a quarter, and 1.9375 beats as a double-dotted quarter tied
222
- * to a dotted thirty-second. The chain knows nothing of barlines or beat
223
- * grouping — an engraver splits further at those — and it uses no tuplets, so a
224
- * length off the plain grid and with no single spelling has no chain either.
228
+ * to a dotted thirty-second. The dot is only written where it leaves the chain
229
+ * in longest-first order, so 10 beats reads as two whole notes tied to a half
230
+ * rather than as a whole note tied to a longer dotted whole. The chain knows
231
+ * nothing of barlines or beat grouping — an engraver splits further at those —
232
+ * and it uses no tuplets, so a length off the plain grid and with no single
233
+ * spelling has no chain either.
225
234
  *
226
235
  * @param beats The length in beats.
227
236
  * @param options `beatUnit` sets what one beat is; a quarter note by default.
@@ -246,16 +255,18 @@ declare function beatsToTiedDurations(beats: number, options?: {
246
255
  * @category Core
247
256
  */
248
257
  type IndexedNoteEvent = {
249
- note: Readonly<NoteEvent>;
250
- originalIndex: number;
251
- endBeat: number;
258
+ readonly note: Readonly<NoteEvent>;
259
+ readonly originalIndex: number;
260
+ readonly endBeat: number;
252
261
  };
253
262
  /**
254
263
  * Which note wins when several sound from the same onset.
255
264
  *
256
265
  * `'highest'` and `'lowest'` name a voice, so the answer does not depend on the
257
266
  * order the caller happened to store the chord in; `'last'` takes the one
258
- * latest in the input array.
267
+ * latest in the input array. Two notes of the same pitch name no voice apart,
268
+ * so both voice tie-breaks settle a unison on the lower `originalIndex` and
269
+ * name the same note.
259
270
  *
260
271
  * @category Core
261
272
  */
@@ -300,9 +311,10 @@ type NoteEventIndexOptions = {
300
311
  name?: string;
301
312
  };
302
313
  /**
303
- * Validate and stable-sort note events once, then expose logarithmic onset and
304
- * active-note lookups. Non-positive-duration notes may be retained for callers
305
- * that intentionally filter them later, but never count as sounding.
314
+ * Validate and stable-sort note events once, then expose onset and active-note
315
+ * lookups that cost `O(log n + k)`, where `k` is how many notes share the onset
316
+ * the answer comes from. Non-positive-duration notes may be retained for
317
+ * callers that intentionally filter them later, but never count as sounding.
306
318
  *
307
319
  * @category Core
308
320
  */
@@ -356,7 +368,8 @@ type TempoMap = readonly TempoEvent[];
356
368
  * @param beat Position in quarter-note beats.
357
369
  * @param map The tempo map; its first event is the time origin.
358
370
  * @returns Elapsed seconds, 0 at the map's first event and negative before it.
359
- * @throws If the map is empty, unsorted, or carries a non-positive tempo.
371
+ * @throws If the map is empty, unsorted, or carries a tempo outside 0.1..1000
372
+ * quarter-note beats per minute.
360
373
  * @example
361
374
  * ```ts
362
375
  * import { beatsToSeconds } from '@libraz/libcantus';
@@ -433,7 +446,9 @@ declare function tempoAt(beat: number, map: TempoMap): number;
433
446
  * back quantized.
434
447
  *
435
448
  * A position before the first downbeat is a negative tick count, which is how a
436
- * pickup crosses the MIDI boundary and comes back.
449
+ * pickup crosses the MIDI boundary and comes back. A position that rounds to
450
+ * the downbeat from below comes back as positive zero: `-0` and `0` are the
451
+ * same tick, and only one of them keys as it under `Object.is`.
437
452
  *
438
453
  * @param beat Position or length in quarter-note beats.
439
454
  * @param ppq Pulses (ticks) per quarter note, e.g. 96, 480, or 960.
@@ -628,7 +643,7 @@ declare function ratioToCents(numerator: number, denominator: number): number;
628
643
  *
629
644
  * @category Pitch & Intervals
630
645
  */
631
- declare const JUST_RATIOS: Readonly<Record<number, readonly [number, number]>>;
646
+ declare const JUST_RATIOS: Readonly<Record<0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12, readonly [number, number]>>;
632
647
  /**
633
648
  * Cents by which a five-limit just interval departs from its 12-TET tempering
634
649
  * (positive means the just interval is wider).