@libraz/libcantus 0.9.5 → 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.
Files changed (185) hide show
  1. package/README.md +61 -270
  2. package/README_ja.md +61 -214
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +200 -353
  6. package/dist/analyze/index.d.ts +200 -353
  7. package/dist/analyze/index.js +56 -9
  8. package/dist/chunk-3JXCW6DX.cjs +3220 -0
  9. package/dist/chunk-3JXCW6DX.cjs.map +1 -0
  10. package/dist/chunk-4A2RMUCC.js +7647 -0
  11. package/dist/chunk-4A2RMUCC.js.map +1 -0
  12. package/dist/chunk-6XPTBE7Y.cjs +49 -0
  13. package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
  14. package/dist/chunk-AFM2ULJH.cjs +5121 -0
  15. package/dist/chunk-AFM2ULJH.cjs.map +1 -0
  16. package/dist/chunk-E3F4AHAH.cjs +690 -0
  17. package/dist/chunk-E3F4AHAH.cjs.map +1 -0
  18. package/dist/{chunk-2PTFGVD7.js → chunk-ELCTZUJJ.js} +250 -132
  19. package/dist/chunk-ELCTZUJJ.js.map +1 -0
  20. package/dist/chunk-FVSI36UV.js +690 -0
  21. package/dist/chunk-FVSI36UV.js.map +1 -0
  22. package/dist/chunk-GEDTJHAC.cjs +1 -0
  23. package/dist/chunk-GEDTJHAC.cjs.map +1 -0
  24. package/dist/chunk-GOZG57ER.js +3220 -0
  25. package/dist/chunk-GOZG57ER.js.map +1 -0
  26. package/dist/chunk-H3VORETI.js +2243 -0
  27. package/dist/chunk-H3VORETI.js.map +1 -0
  28. package/dist/chunk-H42UHPYW.js +1577 -0
  29. package/dist/chunk-H42UHPYW.js.map +1 -0
  30. package/dist/chunk-HFPS7JFD.js +1 -0
  31. package/dist/chunk-INHVXP6Z.js +3053 -0
  32. package/dist/chunk-INHVXP6Z.js.map +1 -0
  33. package/dist/chunk-K4ZJUGF4.cjs +379 -0
  34. package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
  35. package/dist/chunk-KNJEKLKV.js +49 -0
  36. package/dist/chunk-KNJEKLKV.js.map +1 -0
  37. package/dist/chunk-M4CWTJWJ.cjs +7647 -0
  38. package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
  39. package/dist/chunk-MXO72PLY.cjs +211 -0
  40. package/dist/chunk-MXO72PLY.cjs.map +1 -0
  41. package/dist/chunk-NLEU4QSE.cjs +360 -0
  42. package/dist/chunk-NLEU4QSE.cjs.map +1 -0
  43. package/dist/chunk-OE6WK2Y2.cjs +1577 -0
  44. package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
  45. package/dist/chunk-Q2WGKJNN.cjs +1064 -0
  46. package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
  47. package/dist/chunk-QYQQ5J6E.js +1 -0
  48. package/dist/chunk-QYQQ5J6E.js.map +1 -0
  49. package/dist/chunk-RHIJ3S4I.cjs +1 -0
  50. package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
  51. package/dist/chunk-TTCLJX6D.js +211 -0
  52. package/dist/chunk-TTCLJX6D.js.map +1 -0
  53. package/dist/chunk-UADYAYAT.cjs +3053 -0
  54. package/dist/chunk-UADYAYAT.cjs.map +1 -0
  55. package/dist/chunk-VOHZB45S.js +5121 -0
  56. package/dist/chunk-VOHZB45S.js.map +1 -0
  57. package/dist/chunk-VTI6FOLB.js +1064 -0
  58. package/dist/chunk-VTI6FOLB.js.map +1 -0
  59. package/dist/chunk-WWURE4XT.cjs +2243 -0
  60. package/dist/chunk-WWURE4XT.cjs.map +1 -0
  61. package/dist/chunk-YH3BIKHH.js +360 -0
  62. package/dist/chunk-YH3BIKHH.js.map +1 -0
  63. package/dist/core/index.cjs +93 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +87 -346
  66. package/dist/core/index.d.ts +87 -346
  67. package/dist/core/index.js +94 -7
  68. package/dist/generate/index.cjs +89 -10
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +436 -1094
  71. package/dist/generate/index.d.ts +436 -1094
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-Cfx8xgH0.d.ts → index-B4o55NDr.d.cts} +13 -9
  75. package/dist/index-B9MNZboQ.d.cts +770 -0
  76. package/dist/index-B9MNZboQ.d.ts +770 -0
  77. package/dist/index-BGFW-8P2.d.cts +2307 -0
  78. package/dist/index-BO6xE9YU.d.cts +1804 -0
  79. package/dist/index-BkGmlyI3.d.cts +470 -0
  80. package/dist/index-CCNJRG-P.d.cts +1095 -0
  81. package/dist/index-CGpRv5sl.d.cts +643 -0
  82. package/dist/index-C_8o--VE.d.cts +1242 -0
  83. package/dist/index-CgHqxdps.d.ts +2307 -0
  84. package/dist/index-Chbst3Jx.d.ts +1343 -0
  85. package/dist/index-Cj49F892.d.ts +470 -0
  86. package/dist/{index-x2vcsduE.d.cts → index-CunYKLiu.d.ts} +13 -9
  87. package/dist/index-D57QRbVn.d.ts +1804 -0
  88. package/dist/index-DInQqT8Q.d.cts +66 -0
  89. package/dist/index-DInQqT8Q.d.ts +66 -0
  90. package/dist/index-Dzr13q3d.d.cts +1343 -0
  91. package/dist/index-G1uwGGWQ.d.ts +643 -0
  92. package/dist/index-szBv3496.d.ts +1242 -0
  93. package/dist/index.cjs +348 -15
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +17 -15
  96. package/dist/index.d.ts +17 -15
  97. package/dist/index.js +397 -64
  98. package/dist/masks-DYbE485J.d.cts +445 -0
  99. package/dist/masks-DYbE485J.d.ts +445 -0
  100. package/dist/model/index.cjs +39 -7
  101. package/dist/model/index.cjs.map +1 -1
  102. package/dist/model/index.d.cts +4659 -249
  103. package/dist/model/index.d.ts +4659 -249
  104. package/dist/model/index.js +40 -8
  105. package/dist/positional-Cptlj-xs.d.ts +287 -0
  106. package/dist/positional-DvH_sd8u.d.cts +287 -0
  107. package/dist/theory/index.cjs +101 -7
  108. package/dist/theory/index.cjs.map +1 -1
  109. package/dist/theory/index.d.cts +511 -171
  110. package/dist/theory/index.d.ts +511 -171
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +6 -4
  113. package/dist/chunk-2A2QMA74.cjs +0 -261
  114. package/dist/chunk-2A2QMA74.cjs.map +0 -1
  115. package/dist/chunk-2PTFGVD7.js.map +0 -1
  116. package/dist/chunk-357ZDPVU.cjs +0 -162
  117. package/dist/chunk-357ZDPVU.cjs.map +0 -1
  118. package/dist/chunk-3KX76RLG.js +0 -1548
  119. package/dist/chunk-3KX76RLG.js.map +0 -1
  120. package/dist/chunk-4VZ5MZAM.cjs +0 -889
  121. package/dist/chunk-4VZ5MZAM.cjs.map +0 -1
  122. package/dist/chunk-7EONYVI5.js +0 -1204
  123. package/dist/chunk-7EONYVI5.js.map +0 -1
  124. package/dist/chunk-7JEP3MFY.js +0 -162
  125. package/dist/chunk-7JEP3MFY.js.map +0 -1
  126. package/dist/chunk-AID75RN3.js +0 -3176
  127. package/dist/chunk-AID75RN3.js.map +0 -1
  128. package/dist/chunk-BSLTLGM3.cjs +0 -497
  129. package/dist/chunk-BSLTLGM3.cjs.map +0 -1
  130. package/dist/chunk-CU6VG2YM.cjs +0 -109
  131. package/dist/chunk-CU6VG2YM.cjs.map +0 -1
  132. package/dist/chunk-DZMHIGI4.js +0 -337
  133. package/dist/chunk-DZMHIGI4.js.map +0 -1
  134. package/dist/chunk-EDOTEABK.js +0 -109
  135. package/dist/chunk-EDOTEABK.js.map +0 -1
  136. package/dist/chunk-HN2OEUN2.js +0 -33
  137. package/dist/chunk-HN2OEUN2.js.map +0 -1
  138. package/dist/chunk-KBTTEX7Q.js +0 -497
  139. package/dist/chunk-KBTTEX7Q.js.map +0 -1
  140. package/dist/chunk-KQN2ZYND.cjs +0 -724
  141. package/dist/chunk-KQN2ZYND.cjs.map +0 -1
  142. package/dist/chunk-MPTVJIVG.cjs +0 -3176
  143. package/dist/chunk-MPTVJIVG.cjs.map +0 -1
  144. package/dist/chunk-MTMZTMWM.cjs +0 -53
  145. package/dist/chunk-MTMZTMWM.cjs.map +0 -1
  146. package/dist/chunk-N6IHQUOX.js +0 -1
  147. package/dist/chunk-NNB67XA2.js +0 -724
  148. package/dist/chunk-NNB67XA2.js.map +0 -1
  149. package/dist/chunk-OL3IHHSJ.cjs +0 -1204
  150. package/dist/chunk-OL3IHHSJ.cjs.map +0 -1
  151. package/dist/chunk-PI3UXRWK.cjs +0 -528
  152. package/dist/chunk-PI3UXRWK.cjs.map +0 -1
  153. package/dist/chunk-R2VVFAIN.js +0 -528
  154. package/dist/chunk-R2VVFAIN.js.map +0 -1
  155. package/dist/chunk-T64C7ZD7.cjs +0 -1
  156. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  157. package/dist/chunk-WFDXOLUB.cjs +0 -337
  158. package/dist/chunk-WFDXOLUB.cjs.map +0 -1
  159. package/dist/chunk-WTAN4ACG.cjs +0 -1548
  160. package/dist/chunk-WTAN4ACG.cjs.map +0 -1
  161. package/dist/chunk-YFJXS3AU.cjs +0 -33
  162. package/dist/chunk-YFJXS3AU.cjs.map +0 -1
  163. package/dist/chunk-YUE6TJ4M.js +0 -889
  164. package/dist/chunk-YUE6TJ4M.js.map +0 -1
  165. package/dist/chunk-ZGLJM4A4.js +0 -53
  166. package/dist/chunk-ZGLJM4A4.js.map +0 -1
  167. package/dist/function-CxixTB02.d.cts +0 -299
  168. package/dist/function-DTCt675z.d.ts +0 -299
  169. package/dist/index-B2Ulxxpo.d.cts +0 -144
  170. package/dist/index-BkYJ0xMq.d.cts +0 -152
  171. package/dist/index-BuaB-ypt.d.ts +0 -144
  172. package/dist/index-CAN7v-kw.d.ts +0 -199
  173. package/dist/index-COw1Ky9g.d.ts +0 -242
  174. package/dist/index-DRtIhlJj.d.ts +0 -152
  175. package/dist/index-DXd9JikN.d.cts +0 -246
  176. package/dist/index-DXd9JikN.d.ts +0 -246
  177. package/dist/index-alrRninU.d.cts +0 -199
  178. package/dist/index-oTQbYYIs.d.cts +0 -242
  179. package/dist/index-tR9jTn26.d.cts +0 -275
  180. package/dist/index-tR9jTn26.d.ts +0 -275
  181. package/dist/styled-Bl0ZK28h.d.ts +0 -543
  182. package/dist/styled-Crv6SA4S.d.cts +0 -543
  183. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  184. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  185. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -1,213 +1,107 @@
1
- import { N as NoteEvent } from '../types-BN6_Q9Kl.js';
2
- export { K as KeyScale } from '../types-BN6_Q9Kl.js';
3
- import { T as TimeSignature } from '../index-DXd9JikN.js';
4
- export { B as BarPosition, b as barPositionToBeat, a as barPositionToPulse, c as beatToBarPosition, d as beatsPerBar, f as formatBarPosition, e as formatTimeSignature, i as isCompound, g as isStrongBeat, m as metricWeight, p as parseTimeSignature, h as pulseBeats, j as pulsesPerBar, t as tuplet } from '../index-DXd9JikN.js';
5
- export { I as IntervalQualityLabel, N as NoteData, S as SpelledInterval, d as diatonicLetterOf, f as formatNote, i as intervalSemitones, m as midiToNote, n as naturalPitchClassOf, a as noteToMidi, b as noteToPitchClass, p as parseInterval, c as parseNote, e as pitchClassOf, s as spelledInterval, t as transposeByInterval, g as transposeNote } from '../index-tR9jTn26.js';
1
+ export { D as DurationData, I as IndexedNoteEvent, J as JUST_RATIOS, N as NOTE_VALUES, a as NoteEventIndex, b as NoteEventIndexOptions, c as NotePlacement, d as NoteValue, O as OnsetTieBreak, P as PlayabilityIssue, e as PlayabilityIssueType, f as PlayabilityLayer, g as PlayabilityReport, S as SpelledDuration, T as TWELVE_TET, h as TempoEvent, i as TempoMap, j as TuningTable, k as Tuplet, l as beatsToDuration, m as beatsToSeconds, n as beatsToTicks, o as beatsToTiedDurations, p as centsBetweenFreq, q as centsFromNearestStep, r as centsOfSteps, s as centsToRatio, t as createNoteEventIndex, u as durationToBeats, v as durationToSeconds, w as edo, x as frequencyOf, y as justDeviationCents, z as nearestStep, A as playability, B as ratioToCents, C as secondsToBeats, E as stepOf, F as stepsOfCents, G as tempoAt, H as ticksToBeats } from '../index-G1uwGGWQ.js';
2
+ import { g as NoteEvent } from '../index-B9MNZboQ.js';
3
+ export { A as ARTICULATIONS, a as Articulation, B as BudgetExceededError, I as IntervalLike, b as IntervalQualityLabel, c as InvalidInputError, K as KeyName, d as KeyScale, L as LibcantusError, e as LibcantusErrorCode, N as NoSolutionError, f as NoteData, h as NoteLike, i as NoteNameOptions, j as NoteNameSystem, P as ParseResult, S as SpelledInterval, k as detectNoteNameSystem, l as diatonicLetterOf, m as formatKeyName, n as formatNote, o as intervalSemitones, p as isLibcantusError, q as midiToNote, r as naturalPitchClassOf, s as noteToMidi, t as noteToPitchClass, u as parseInterval, v as parseKeyName, w as parseNote, x as pitchClassOf, y as spelledInterval, z as toNoteData, C as toSpelledInterval, D as transposeByInterval, E as transposeNote, F as tryParseInterval, G as tryParseKeyName, H as tryParseNote } from '../index-B9MNZboQ.js';
4
+ import { f as StringedProfile } from '../positional-Cptlj-xs.js';
5
+ export { I as InstrumentProfile, a as InstrumentProfileCommon, b as InstrumentProfileLike, L as LIMBS, c as Limb, P as PercussionProfile, d as PositionalRng, S as SeedPath, e as StringFingering, g as canSound, h as createPositionalRng, i as deriveSeed, j as fingeringsFor, k as foldIntoRange, l as includeAt, m as instrumentRange } from '../positional-Cptlj-xs.js';
6
+ export { C as ConsonanceClass, c as classifyInterval, i as isConsonantInterval, a as isPerfectInterval } from '../index-DInQqT8Q.js';
7
+ import { c as MeterMap, T as TimeSignature } from '../index-Cj49F892.js';
8
+ export { B as BarPosition, M as MeterChange, a as MeterData, b as MeterLike, d as barIndexAt, e as barPositionToBeat, f as barPositionToPulse, g as barStartBeat, h as beatToBarPosition, i as beatsPerBar, j as beatsPerBarAt, k as formatBarPosition, l as formatTimeSignature, m as isCompound, n as isStrongBeat, o as meterAt, p as metricWeight, q as parseTimeSignature, r as pulseBeats, s as pulsesPerBar, t as resolveMeters, u as toMeterData, v as tryParseTimeSignature, w as tuplet } from '../index-Cj49F892.js';
6
9
 
7
10
  /**
8
- * The library's error taxonomy.
9
- *
10
- * Every failure this library raises falls into one of three kinds, and the kind
11
- * decides what a caller can do about it. Batch-voicing a lead sheet, for
12
- * instance, has to tell "fix the input" apart from "loosen the constraints",
13
- * and a message string is not a contract.
14
- *
15
- * The classes extend the built-in error types a caller would otherwise catch —
16
- * a rejected argument is still a `RangeError` — so the `code` narrows an
17
- * existing category rather than replacing it.
18
- */
19
- /**
20
- * What kind of failure an error reports.
21
- *
22
- * - `INVALID_INPUT`: an argument is malformed or out of range. The caller's
23
- * input must change.
24
- * - `NO_SOLUTION`: the input is valid but nothing satisfies the constraints.
25
- * The constraints must change, or the failure must be accepted.
26
- * - `BUDGET_EXCEEDED`: the requested work is larger than the synchronous
27
- * generation budget. The request must be smaller, or the budget larger.
11
+ * Electric bass in standard tuning: E1 A1 D2 G2 over a 24-fret neck.
28
12
  *
29
13
  * @category Core
30
14
  */
31
- type LibcantusErrorCode = 'INVALID_INPUT' | 'NO_SOLUTION' | 'BUDGET_EXCEEDED';
15
+ declare const BASS_4_STRING: StringedProfile;
32
16
  /**
33
- * A rejected argument: malformed, out of range, or naming something unknown.
17
+ * Five-string bass with the low B: B0 E1 A1 D2 G2.
34
18
  *
35
19
  * @category Core
36
20
  */
37
- declare class InvalidInputError extends RangeError {
38
- readonly code: "INVALID_INPUT";
39
- readonly name = "InvalidInputError";
40
- }
21
+ declare const BASS_5_STRING: StringedProfile;
41
22
  /**
42
- * A valid request with no answer: no voicing fits the given ranges, no chord
43
- * matches the pitches, no preset claims the style.
23
+ * Six-string guitar in standard tuning: E2 A2 D3 G3 B3 E4.
44
24
  *
45
25
  * @category Core
46
26
  */
47
- declare class NoSolutionError extends Error {
48
- readonly code: "NO_SOLUTION";
49
- readonly name = "NoSolutionError";
50
- /**
51
- * Where in a sequence the failure occurred, when the operation was working
52
- * through one — the chord index of a progression, say.
53
- */
54
- readonly at: number | undefined;
55
- constructor(message: string, options?: {
56
- at?: number;
57
- });
58
- }
27
+ declare const GUITAR_STANDARD: StringedProfile;
59
28
  /**
60
- * Work that would exceed the synchronous generation budget.
29
+ * Six-string guitar in drop D: the lowest string down a tone to D2, the rest
30
+ * standard. Only the tuning differs, which is the whole point of deriving the
31
+ * range from it.
61
32
  *
62
33
  * @category Core
63
34
  */
64
- declare class BudgetExceededError extends RangeError {
65
- readonly code: "BUDGET_EXCEEDED";
66
- readonly name = "BudgetExceededError";
67
- }
68
- /**
69
- * Whether a caught value is one of this library's coded errors.
70
- *
71
- * @param value The caught value.
72
- * @returns True when it carries a {@link LibcantusErrorCode}.
73
- * @example
74
- * ```ts
75
- * import { isLibcantusError, voiceChord, parseChordSymbol } from '@libraz/libcantus';
76
- * try {
77
- * voiceChord(parseChordSymbol('C'), { ranges: [] });
78
- * } catch (error) {
79
- * if (isLibcantusError(error) && error.code === 'NO_SOLUTION') {
80
- * // loosen the constraints rather than blaming the input
81
- * }
82
- * }
83
- * ```
84
- * @category Core
85
- */
86
- declare function isLibcantusError(value: unknown): value is InvalidInputError | NoSolutionError | BudgetExceededError;
35
+ declare const GUITAR_DROP_D: StringedProfile;
87
36
 
88
37
  /**
89
- * A validated note retaining its position in the caller's original array.
38
+ * The reproducibility contract for generated output.
90
39
  *
91
- * @category Core
40
+ * A saved project is a seed plus parameters, and it only reopens as the same
41
+ * piece if the library still turns them into the same notes. Package versions
42
+ * cannot carry that promise: a bug fix inside a generator is a patch release
43
+ * and still moves every note. The algorithm version is the separate number
44
+ * that does carry it, declared here and recorded by the caller alongside the
45
+ * seed.
92
46
  */
93
- type IndexedNoteEvent = {
94
- note: Readonly<NoteEvent>;
95
- originalIndex: number;
96
- endBeat: number;
97
- };
98
47
  /**
99
- * Which note wins when several sound from the same onset.
48
+ * The generator algorithm version this build produces by default.
100
49
  *
101
- * `'highest'` and `'lowest'` name a voice, so the answer does not depend on the
102
- * order the caller happened to store the chord in; `'last'` takes the one
103
- * latest in the input array.
50
+ * The guarantee it carries: for a fixed algorithm version, the same inputs —
51
+ * seed, version and every documented parameter — produce the same generated
52
+ * output from any build of this library that accepts that version. It covers
53
+ * what the generators return, and nothing else: analysis results, error
54
+ * messages and output taken under a different version are outside it.
104
55
  *
105
- * @category Core
106
- */
107
- type OnsetTieBreak = 'highest' | 'lowest' | 'last';
108
- /**
109
- * Binary-searchable, stable-sorted index over note onsets and active spans.
56
+ * Changing what a generator returns for a version it already accepts is a
57
+ * defect. New behaviour raises this constant instead, and the older version
58
+ * keeps producing what it produced. Withdrawing an accepted version is a
59
+ * breaking change, and a project pinned to a withdrawn or not-yet-known
60
+ * version is rejected rather than reinterpreted.
110
61
  *
111
- * @category Core
62
+ * @category Utilities
112
63
  */
113
- type NoteEventIndex = {
114
- notes: readonly IndexedNoteEvent[];
115
- /**
116
- * Latest-onset note sounding at `beat`. Simultaneous onsets are resolved by
117
- * the index's {@link OnsetTieBreak}.
118
- */
119
- at: (beat: number) => IndexedNoteEvent | undefined;
120
- /** Whether one or more sounding notes attack at `beat`. */
121
- attacksAt: (beat: number) => boolean;
122
- /** Unique sounding-note attack beats strictly inside `(startBeat, endBeat)`. */
123
- onsetsBetween: (startBeat: number, endBeat: number) => number[];
124
- };
64
+ declare const ALGORITHM_VERSION = 1;
125
65
  /**
126
- * Validate and stable-sort note events once, then expose logarithmic onset and
127
- * active-note lookups. Non-positive-duration notes may be retained for callers
128
- * that intentionally filter them later, but never count as sounding.
66
+ * The oldest algorithm version this build still produces.
129
67
  *
130
- * @category Core
131
- */
132
- declare function createNoteEventIndex(events: readonly NoteEvent[], options?: {
133
- allowNonPositiveDuration?: boolean;
134
- tieBreak?: OnsetTieBreak;
135
- budget?: number;
136
- /** What the events are, for the validation error message. */
137
- name?: string;
138
- }): NoteEventIndex;
139
-
140
- /**
141
- * Counterpoint classification of a harmonic interval by consonance.
142
- *
143
- * Distinct from a spelled interval's quality label — see
144
- * {@link IntervalQualityLabel}, which names the `'P'` / `'M'` / `'m'` / `'A'` /
145
- * `'d'` spelling of an interval rather than how it sounds against another
146
- * voice.
147
- *
148
- * @see {@link IntervalQualityLabel}
149
- * @category Pitch & Intervals
68
+ * @category Utilities
150
69
  */
151
- declare enum ConsonanceClass {
152
- PerfectConsonance = 0,
153
- ImperfectConsonance = 1,
154
- Dissonance = 2
155
- }
70
+ declare const MIN_ALGORITHM_VERSION = 1;
156
71
  /**
157
- * Classify an interval for counterpoint evaluation.
72
+ * Resolve the algorithm version a request runs under.
158
73
  *
159
- * The interval is reduced modulo 12, so compound intervals classify as their
160
- * simple equivalents. The perfect fourth (5) is context-dependent: with
161
- * `twoVoice` (the default) it classifies as a dissonance, matching
162
- * {@link isConsonantInterval}; otherwise it is an imperfect consonance.
74
+ * An unpinned request runs at {@link ALGORITHM_VERSION}, which is what a
75
+ * caller who wants the current behaviour means; a caller who wants the piece
76
+ * back records the resolved number and passes it next time. A version this
77
+ * build does not produce is rejected — a project saved by a newer build cannot
78
+ * be reproduced by an older one, and quietly rendering it at another version
79
+ * would return a different piece under the same name.
163
80
  *
164
- * @param semitones Interval size in semitones (may be negative or compound).
165
- * @param twoVoice When true, the perfect fourth is treated as dissonant.
166
- * @returns The counterpoint quality of the interval.
81
+ * @param requested The pinned version, or undefined for the current one.
82
+ * @returns The version to generate under.
83
+ * @throws If the version is not an integer this build produces.
167
84
  * @example
168
85
  * ```ts
169
- * import { classifyInterval, ConsonanceClass } from '@libraz/libcantus';
170
- * classifyInterval(7); // ConsonanceClass.PerfectConsonance
171
- * classifyInterval(5); // ConsonanceClass.Dissonance (two-voice)
172
- * classifyInterval(5, false); // ConsonanceClass.ImperfectConsonance
86
+ * import { ALGORITHM_VERSION, resolveAlgorithmVersion } from '@libraz/libcantus';
87
+ * resolveAlgorithmVersion(undefined); // ALGORITHM_VERSION
88
+ * resolveAlgorithmVersion(ALGORITHM_VERSION); // the pinned value, unchanged
173
89
  * ```
174
- * @category Pitch & Intervals
175
- */
176
- declare function classifyInterval(semitones: number, twoVoice?: boolean): ConsonanceClass;
177
- /**
178
- * Test whether an interval is a perfect consonance: the unison/octave or the
179
- * fifth.
180
- *
181
- * The perfect fourth is deliberately excluded. This is the predicate the
182
- * parallel-motion rules are written against, where the fourth behaves as a
183
- * dissonance between two voices; {@link classifyInterval} is the general
184
- * classifier, and it treats the fourth as context-dependent.
185
- *
186
- * @param semitones Interval size in semitones (may be negative or compound).
187
- * @returns True for the unison/octave (0) and the perfect fifth (7) mod 12.
188
- * @category Pitch & Intervals
189
- */
190
- declare function isPerfectInterval(semitones: number): boolean;
191
- /**
192
- * Test whether an interval is consonant.
193
- *
194
- * The consonant set is {0, 3, 4, 5, 7, 8, 9} mod 12. In two-voice counterpoint
195
- * the perfect fourth (5) is treated as a dissonance, so `twoVoice` set to true
196
- * excludes it.
197
- *
198
- * @param semitones Interval size in semitones (may be negative or compound).
199
- * @param twoVoice When true, the perfect fourth is treated as dissonant.
200
- * @returns True if the interval is consonant in the given context.
201
- * @category Pitch & Intervals
90
+ * @category Utilities
202
91
  */
203
- declare function isConsonantInterval(semitones: number, twoVoice?: boolean): boolean;
92
+ declare function resolveAlgorithmVersion(requested?: number): number;
204
93
 
205
94
  /**
206
95
  * Deterministic seeded pseudo-random number generation shared across the
207
96
  * generative modules (rhythm, drums, bass, groove, counter-melody). A single
208
97
  * mulberry32 core exposes the small set of samplers those modules need; given a
209
98
  * seed the stream is fully reproducible.
99
+ *
100
+ * Around that core sit the pieces that make a whole project reproducible: seeds
101
+ * derived hierarchically from one project number, draws addressed by position
102
+ * rather than by call order, and the algorithm version the output is pinned to.
210
103
  */
104
+
211
105
  /**
212
106
  * A deterministic PRNG with the sampling helpers the generators need.
213
107
  *
@@ -239,183 +133,6 @@ type Rng = {
239
133
  */
240
134
  declare function createRng(seed: number): Rng;
241
135
 
242
- /**
243
- * Tuning and microtonality: frequency conversion, cents, arbitrary equal
244
- * temperaments (EDO), and just-intonation ratios.
245
- *
246
- * The rest of the library reasons in twelve-tone pitch classes, which is a
247
- * deliberate scope choice for tonal theory. This module is the escape hatch for
248
- * anyone who needs actual frequencies, cents, non-12 equal temperaments, or the
249
- * acoustic (just) tuning behind the tempered intervals.
250
- */
251
- /**
252
- * An equal-tempered tuning: a reference pitch and a number of equal divisions of
253
- * the octave. With `divisions === 12` a step index is an ordinary MIDI number.
254
- *
255
- * @category Pitch & Intervals
256
- */
257
- type Tuning = {
258
- /** Step index (MIDI number when `divisions` is 12) whose frequency is `refFreq`. */
259
- readonly refStep: number;
260
- /** Frequency in Hz of `refStep`. */
261
- readonly refFreq: number;
262
- /** Equal divisions of the octave (12 standard; e.g. 19 or 31 for microtonal). */
263
- readonly divisions: number;
264
- };
265
- /**
266
- * Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.
267
- *
268
- * @category Pitch & Intervals
269
- */
270
- declare const TWELVE_TET: Tuning;
271
- /**
272
- * Build an equal temperament with `n` divisions of the octave.
273
- *
274
- * @param n Divisions of the octave (e.g. 19, 24, 31).
275
- * @param refFreq Reference frequency in Hz (default 440).
276
- * @param refStep Step index of the reference (default 69).
277
- * @returns The tuning.
278
- * @example
279
- * ```ts
280
- * import { edo, frequencyOf } from '@libraz/libcantus';
281
- * const et19 = edo(19); // { refStep: 69, refFreq: 440, divisions: 19 }
282
- * frequencyOf(70, et19); // one 19-EDO step above A4
283
- * ```
284
- * @category Pitch & Intervals
285
- */
286
- declare function edo(n: number, refFreq?: number, refStep?: number): Tuning;
287
- /**
288
- * Frequency in Hz of a step index under a tuning.
289
- *
290
- * @param step Step index (a MIDI number under 12-EDO).
291
- * @param tuning The tuning (default 12-TET).
292
- * @returns The frequency in Hz.
293
- * @example
294
- * ```ts
295
- * import { frequencyOf } from '@libraz/libcantus';
296
- * frequencyOf(69); // 440 (A4 in 12-TET)
297
- * frequencyOf(60); // middle C in Hz
298
- * ```
299
- * @category Pitch & Intervals
300
- */
301
- declare function frequencyOf(step: number, tuning?: Tuning): number;
302
- /**
303
- * Nearest step index to a frequency under a tuning (the inverse of
304
- * {@link frequencyOf}, rounded).
305
- *
306
- * @param freq Frequency in Hz.
307
- * @param tuning The tuning (default 12-TET).
308
- * @returns The nearest step index.
309
- * @category Pitch & Intervals
310
- */
311
- declare function nearestStep(freq: number, tuning?: Tuning): number;
312
- /**
313
- * Step index of a frequency under a tuning, unrounded.
314
- *
315
- * The exact inverse of {@link frequencyOf}. {@link nearestStep} rounds it,
316
- * which discards how far off the pitch actually is — the figure a tuner
317
- * display, a pitch-bend amount, or an intonation check is built on.
318
- *
319
- * @param freq Frequency in Hz.
320
- * @param tuning The tuning (default 12-TET).
321
- * @returns The fractional step index.
322
- * @example
323
- * ```ts
324
- * import { stepOf } from '@libraz/libcantus';
325
- * stepOf(442); // slightly above 69, the step for A440
326
- * ```
327
- * @category Pitch & Intervals
328
- */
329
- declare function stepOf(freq: number, tuning?: Tuning): number;
330
- /**
331
- * How far a frequency sits from its nearest step, in cents.
332
- *
333
- * Positive means sharp of the step, negative flat.
334
- *
335
- * @param freq Frequency in Hz.
336
- * @param tuning The tuning (default 12-TET).
337
- * @returns The signed deviation in cents.
338
- * @example
339
- * ```ts
340
- * import { centsFromNearestStep } from '@libraz/libcantus';
341
- * Math.round(centsFromNearestStep(442)); // 8
342
- * ```
343
- * @category Pitch & Intervals
344
- */
345
- declare function centsFromNearestStep(freq: number, tuning?: Tuning): number;
346
- /**
347
- * Steps spanned by an interval in cents, unrounded — the inverse of
348
- * {@link centsOfSteps}.
349
- *
350
- * @param cents The interval in cents.
351
- * @param tuning The tuning (default 12-TET).
352
- * @returns The fractional step count.
353
- * @category Pitch & Intervals
354
- */
355
- declare function stepsOfCents(cents: number, tuning?: Tuning): number;
356
- /**
357
- * The frequency ratio an interval in cents spans — the inverse of
358
- * {@link ratioToCents}.
359
- *
360
- * Multiply a frequency by this to move it by that many cents, which is how a
361
- * cents offset (a pitch-bend spec, a tuning table entry) is applied.
362
- *
363
- * @param cents The interval in cents.
364
- * @returns The frequency ratio.
365
- * @example
366
- * ```ts
367
- * import { centsToRatio } from '@libraz/libcantus';
368
- * 440 * centsToRatio(14); // A440 raised by 14 cents
369
- * ```
370
- * @category Pitch & Intervals
371
- */
372
- declare function centsToRatio(cents: number): number;
373
- /**
374
- * Interval in cents between two frequencies.
375
- *
376
- * @param a Lower/first frequency in Hz.
377
- * @param b Upper/second frequency in Hz.
378
- * @returns Cents from `a` to `b` (negative if `b` is lower).
379
- * @category Pitch & Intervals
380
- */
381
- declare function centsBetweenFreq(a: number, b: number): number;
382
- /**
383
- * Cents spanned by a number of equal-temperament steps.
384
- *
385
- * @param steps Number of steps.
386
- * @param tuning The tuning (default 12-TET).
387
- * @returns The cents.
388
- * @category Pitch & Intervals
389
- */
390
- declare function centsOfSteps(steps: number, tuning?: Tuning): number;
391
- /**
392
- * Cents of a frequency ratio, e.g. `ratioToCents(3, 2)` ≈ 701.955 for the just
393
- * perfect fifth.
394
- *
395
- * @param numerator Ratio numerator.
396
- * @param denominator Ratio denominator.
397
- * @returns The interval in cents.
398
- * @category Pitch & Intervals
399
- */
400
- declare function ratioToCents(numerator: number, denominator: number): number;
401
- /**
402
- * Five-limit just-intonation ratios for the twelve interval classes above a
403
- * unison, indexed by semitone class (0..12).
404
- *
405
- * @category Pitch & Intervals
406
- */
407
- declare const JUST_RATIOS: Readonly<Record<number, readonly [number, number]>>;
408
- /**
409
- * Cents by which a five-limit just interval departs from its 12-TET tempering
410
- * (positive means the just interval is wider).
411
- *
412
- * @param semitoneClass Semitone class in [0, 12].
413
- * @returns The deviation in cents.
414
- * @throws If `semitoneClass` is not an integer in [0, 12].
415
- * @category Pitch & Intervals
416
- */
417
- declare function justDeviationCents(semitoneClass: number): number;
418
-
419
136
  /**
420
137
  * Default upper bound for synchronous event/window/candidate generation.
421
138
  *
@@ -438,7 +155,11 @@ declare function assertInteger(value: number, name: string, min?: number, max?:
438
155
  declare function assertMidiPitch(value: number, name?: string): number;
439
156
  /** Clamp an integer MIDI pitch to the inclusive range 0..127. */
440
157
  declare function clampToMidi(value: number, name?: string): number;
441
- /** Require a bounded integer scale/chord degree. */
158
+ /**
159
+ * Require a bounded scale/chord degree, counted from 1 the way musicians count
160
+ * degrees: 1 is the tonic, 5 the dominant. Zero and negative degrees name no
161
+ * degree at all and are rejected.
162
+ */
442
163
  declare function assertDegree(value: number, name?: string): number;
443
164
  /** Require a finite semitone offset before pitch-class reduction. */
444
165
  declare function assertFiniteSemitones(value: number, name?: string): number;
@@ -503,6 +224,17 @@ declare function assertGenerationBudget(estimated: number, name: string, limit?:
503
224
  * @category Core
504
225
  */
505
226
  declare function assertTimeSignature(ts: TimeSignature, name?: string): TimeSignature;
227
+ /**
228
+ * Validate a meter map: a non-empty run of time signatures, each with the beat
229
+ * it takes effect at, in strictly increasing beat order.
230
+ *
231
+ * The first entry also governs everything before it, so a map need not start at
232
+ * beat 0 — that is what lets a pickup at a negative beat be read in the
233
+ * signature the piece opens in.
234
+ *
235
+ * @category Core
236
+ */
237
+ declare function assertMeterMap(meters: MeterMap, name?: string): MeterMap;
506
238
  /**
507
239
  * How strictly {@link assertNoteEvent} and {@link assertNoteEvents} read an
508
240
  * event array.
@@ -517,6 +249,15 @@ type NoteEventAssertOptions = {
517
249
  allowNonPositiveDuration?: boolean;
518
250
  /** Upper bound on the event count; defaults to the generation budget. */
519
251
  budget?: number;
252
+ /**
253
+ * Earliest accepted onset. Onsets are unbounded below by default, because a
254
+ * pickup sounds before the downbeat and the downbeat is beat 0; pass
255
+ * `-pickupBeats` to reject a note starting earlier than the pickup a caller
256
+ * has declared.
257
+ *
258
+ * @defaultValue `-Number.MAX_SAFE_INTEGER`
259
+ */
260
+ minStartBeat?: number;
520
261
  };
521
262
  /**
522
263
  * Validate the finite fields of one timeline note event.
@@ -531,4 +272,4 @@ declare function assertNoteEvent(event: NoteEvent, name?: string, options?: Note
531
272
  */
532
273
  declare function assertNoteEvents(events: readonly NoteEvent[], name?: string, options?: NoteEventAssertOptions): NoteEvent[];
533
274
 
534
- export { BudgetExceededError, ConsonanceClass, DEFAULT_GENERATION_BUDGET, type IndexedNoteEvent, InvalidInputError, JUST_RATIOS, type LibcantusErrorCode, NoSolutionError, NoteEvent, type NoteEventAssertOptions, type NoteEventIndex, type OnsetTieBreak, type Rng, TWELVE_TET, TimeSignature, type Tuning, assertDegree, assertFiniteNumber, assertFiniteSemitones, assertGenerationBudget, assertInteger, assertMidiPitch, assertNoteEvent, assertNoteEvents, assertOneOf, assertPositiveInt, assertRange, assertTimeSignature, centsBetweenFreq, centsFromNearestStep, centsOfSteps, centsToRatio, clampToMidi, classifyInterval, createNoteEventIndex, createRng, dropSilentNotes, edo, frequencyOf, isConsonantInterval, isLibcantusError, isPerfectInterval, justDeviationCents, nearestStep, ratioToCents, soundingNotesOnly, stepOf, stepsOfCents };
275
+ export { ALGORITHM_VERSION, BASS_4_STRING, BASS_5_STRING, DEFAULT_GENERATION_BUDGET, GUITAR_DROP_D, GUITAR_STANDARD, MIN_ALGORITHM_VERSION, MeterMap, NoteEvent, type NoteEventAssertOptions, type Rng, StringedProfile, TimeSignature, assertDegree, assertFiniteNumber, assertFiniteSemitones, assertGenerationBudget, assertInteger, assertMeterMap, assertMidiPitch, assertNoteEvent, assertNoteEvents, assertOneOf, assertPositiveInt, assertRange, assertTimeSignature, clampToMidi, createRng, dropSilentNotes, resolveAlgorithmVersion, soundingNotesOnly };