@libraz/libcantus 1.0.1 → 1.2.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-35NBV2FT.cjs +145 -0
  9. package/dist/chunk-35NBV2FT.cjs.map +1 -0
  10. package/dist/{chunk-UADYAYAT.cjs → chunk-3R3QVKY5.cjs} +1353 -580
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-WWURE4XT.cjs → chunk-76ZBTRG2.cjs} +624 -729
  13. package/dist/chunk-76ZBTRG2.cjs.map +1 -0
  14. package/dist/{chunk-FVSI36UV.js → chunk-7KG3RTYS.js} +197 -110
  15. package/dist/chunk-7KG3RTYS.js.map +1 -0
  16. package/dist/{chunk-VTI6FOLB.js → chunk-AGOVW7U6.js} +709 -133
  17. package/dist/chunk-AGOVW7U6.js.map +1 -0
  18. package/dist/{chunk-E3F4AHAH.cjs → chunk-CGBADIO5.cjs} +221 -134
  19. package/dist/chunk-CGBADIO5.cjs.map +1 -0
  20. package/dist/{chunk-6XH6QZYQ.cjs → chunk-DSMJRXCZ.cjs} +1455 -849
  21. package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
  22. package/dist/{chunk-PKFNUHDY.js → chunk-FBH75DAY.js} +1218 -612
  23. package/dist/chunk-FBH75DAY.js.map +1 -0
  24. package/dist/chunk-H2KJHBW4.js +3329 -0
  25. package/dist/chunk-H2KJHBW4.js.map +1 -0
  26. package/dist/chunk-HPVSH7SF.cjs +2294 -0
  27. package/dist/chunk-HPVSH7SF.cjs.map +1 -0
  28. package/dist/{chunk-MXO72PLY.cjs → chunk-HWHGOVO6.cjs} +21 -19
  29. package/dist/chunk-HWHGOVO6.cjs.map +1 -0
  30. package/dist/chunk-HYJJ2V2I.js +2294 -0
  31. package/dist/chunk-HYJJ2V2I.js.map +1 -0
  32. package/dist/{chunk-AFM2ULJH.cjs → chunk-J7FHVZII.cjs} +1567 -1067
  33. package/dist/chunk-J7FHVZII.cjs.map +1 -0
  34. package/dist/chunk-K6OZXIR6.js +1 -0
  35. package/dist/{chunk-H3VORETI.js → chunk-M5RYMAFI.js} +487 -592
  36. package/dist/chunk-M5RYMAFI.js.map +1 -0
  37. package/dist/chunk-NB25P3EH.js +363 -0
  38. package/dist/chunk-NB25P3EH.js.map +1 -0
  39. package/dist/{chunk-TTCLJX6D.js → chunk-NFJ4P6ZZ.js} +10 -8
  40. package/dist/chunk-NFJ4P6ZZ.js.map +1 -0
  41. package/dist/chunk-NM7UAMC3.cjs +457 -0
  42. package/dist/chunk-NM7UAMC3.cjs.map +1 -0
  43. package/dist/chunk-PM4XJE2Y.cjs +363 -0
  44. package/dist/chunk-PM4XJE2Y.cjs.map +1 -0
  45. package/dist/chunk-PUKJP2Z5.js +145 -0
  46. package/dist/chunk-PUKJP2Z5.js.map +1 -0
  47. package/dist/{chunk-VOHZB45S.js → chunk-Q6LFSAKV.js} +1321 -821
  48. package/dist/chunk-Q6LFSAKV.js.map +1 -0
  49. package/dist/{chunk-INHVXP6Z.js → chunk-RFIBF2RR.js} +1203 -430
  50. package/dist/chunk-RFIBF2RR.js.map +1 -0
  51. package/dist/{chunk-Q2WGKJNN.cjs → chunk-S5BKZZGM.cjs} +699 -123
  52. package/dist/chunk-S5BKZZGM.cjs.map +1 -0
  53. package/dist/chunk-SGLEQVJH.cjs +3329 -0
  54. package/dist/chunk-SGLEQVJH.cjs.map +1 -0
  55. package/dist/chunk-UGLFDG7L.cjs +1 -0
  56. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  57. package/dist/{chunk-YH3BIKHH.js → chunk-UMWZ4ZET.js} +146 -49
  58. package/dist/chunk-UMWZ4ZET.js.map +1 -0
  59. package/dist/{index-szBv3496.d.ts → coerce-DP0vYu-x.d.ts} +286 -199
  60. package/dist/{index-C_8o--VE.d.cts → coerce-DRERzdve.d.cts} +286 -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-CCNJRG-P.d.cts → index-B9QLziDe.d.cts} +293 -56
  72. package/dist/{index-Dzr13q3d.d.cts → index-BBWNZS0f.d.cts} +135 -58
  73. package/dist/{index-9wcMhG73.d.ts → index-BHIUyjhZ.d.ts} +293 -56
  74. package/dist/{index-BGFW-8P2.d.cts → index-BJgP97N1.d.ts} +196 -114
  75. package/dist/{index-CunYKLiu.d.ts → index-BL5N2Oyz.d.cts} +1 -1
  76. package/dist/{index-Cj49F892.d.ts → index-BMweUlm5.d.cts} +63 -23
  77. package/dist/{index-CgHqxdps.d.ts → index-BNSM-DSk.d.cts} +196 -114
  78. package/dist/{index-D57QRbVn.d.ts → index-BRMbPkHE.d.ts} +480 -254
  79. package/dist/{index-Chbst3Jx.d.ts → index-CJBBiAmU.d.ts} +135 -58
  80. package/dist/{index-BkGmlyI3.d.cts → index-CL4b4gUQ.d.ts} +63 -23
  81. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  82. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  83. package/dist/{index-BO6xE9YU.d.cts → index-DGCNKAUr.d.cts} +480 -254
  84. package/dist/{index-B4o55NDr.d.cts → index-Dw2DP7vm.d.ts} +1 -1
  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.cts → masks-Cc7-MzzY.d.ts} +49 -2
  92. package/dist/{masks-DYbE485J.d.ts → masks-pADfTOiD.d.cts} +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 +504 -182
  96. package/dist/model/index.d.ts +504 -182
  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 +7 -5
  106. package/dist/chunk-2LCPM3EG.js +0 -3224
  107. package/dist/chunk-2LCPM3EG.js.map +0 -1
  108. package/dist/chunk-6XH6QZYQ.cjs.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-B6XNKWZV.cjs +0 -3224
  113. package/dist/chunk-B6XNKWZV.cjs.map +0 -1
  114. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  115. package/dist/chunk-ELCTZUJJ.js +0 -379
  116. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  117. package/dist/chunk-FVSI36UV.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-MXO72PLY.cjs.map +0 -1
  128. package/dist/chunk-NLEU4QSE.cjs +0 -360
  129. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  130. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  131. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  132. package/dist/chunk-PKFNUHDY.js.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 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/libcantus/libcantus/dist/chunk-6XH6QZYQ.cjs","../src/model/interval.ts","../src/model/note.ts","../src/model/shared.ts","../src/model/timeline.ts","../src/model/progression.ts","../src/model/chord.ts","../src/model/key.ts","../src/model/score.ts","../src/model/arrangement.ts","../src/model/instrument.ts","../src/model/composer.ts","../src/model/duration.ts","../src/model/meter.ts","../src/model/motif.ts","../src/model/rhythm.ts","../src/model/tempo.ts","../src/model/tuning.ts","../src/model/voicing.ts"],"names":["track"],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACvNA,IAAM,cA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","file":"/home/runner/work/libcantus/libcantus/dist/chunk-6XH6QZYQ.cjs","sourcesContent":[null,"import { type ParseResult, unwrapParse } from '../core/errors/index.js';\nimport { isConsonantInterval } from '../core/interval/index.js';\nimport type { IntervalQualityLabel, SpelledInterval } from '../core/pitch/index.js';\nimport { intervalSemitones, toSpelledInterval, tryParseInterval } from '../core/pitch/index.js';\nimport type { Note } from './note.js';\n\n/** Number of diatonic degrees an interval and its inversion span together. */\nconst INVERSION_SUM = 9;\n\n/** Diatonic size of an octave, the largest simple interval. */\nconst OCTAVE_DEGREES = 8;\n\n/**\n * The simple interval a diatonic number belongs to.\n *\n * Seven is subtracted while the number is larger than an octave, so the octave\n * and its multiples (8, 15, 22, ...) reduce to the octave rather than to the\n * unison: plain modulo-7 arithmetic folds them onto 1 and would then invert a\n * double octave as if it were a unison.\n */\nfunction simpleDegree(numberValue: number): number {\n const reduced = ((numberValue - 1) % 7) + 1;\n return reduced === 1 && numberValue > 1 ? OCTAVE_DEGREES : reduced;\n}\n\n/** The quality an interval's inversion carries. */\nfunction invertQuality(quality: IntervalQualityLabel): IntervalQualityLabel {\n if (quality === 'P') {\n return 'P';\n }\n if (quality === 'M') {\n return 'm';\n }\n if (quality === 'm') {\n return 'M';\n }\n return quality.startsWith('A')\n ? (`d${'d'.repeat(quality.length - 1)}` as IntervalQualityLabel)\n : (`A${'A'.repeat(quality.length - 1)}` as IntervalQualityLabel);\n}\n\n/**\n * An immutable spelled interval value: a diatonic number, a quality label, and\n * a signed semitone span. A thin convenience wrapper over the pitch module's\n * plain interval result.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Interval, Note } from '@libraz/libcantus';\n * Interval.between(Note.parse('C4'), Note.parse('G4')).name; // 'P5'\n * ```\n */\nexport class Interval {\n readonly #number: number;\n readonly #quality: IntervalQualityLabel;\n readonly #semitones: number;\n readonly #descending: boolean;\n\n private constructor(\n numberValue: number,\n quality: IntervalQualityLabel,\n semitones: number,\n descending = semitones < 0,\n ) {\n this.#number = numberValue;\n this.#quality = quality;\n this.#semitones = semitones;\n this.#descending = descending;\n }\n\n /**\n * The spelled interval between two notes.\n *\n * @param a The first note.\n * @param b The second note.\n * @returns The interval from `a` to `b`.\n */\n static between(a: Note, b: Note): Interval {\n return a.intervalTo(b);\n }\n\n /**\n * Build an interval from explicit components.\n *\n * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.\n * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.\n * @param semitones Signed semitone span; its magnitude must be the span the\n * number and quality describe.\n * @returns The interval.\n * @throws If the three components do not describe the same interval — a\n * `P5` spanning 8 semitones is not a value any other method can produce.\n */\n static of(numberValue: number, quality: IntervalQualityLabel, semitones: number): Interval {\n return Interval.fromData({ number: numberValue, quality, semitones });\n }\n\n /**\n * Wrap a plain spelled interval, as returned by the pitch module.\n *\n * The data is checked the way {@link Interval.of} checks its arguments: a\n * deserialized interval enters through here, and an unchecked one would be a\n * value no measurement produces — a `P5` spanning eight semitones names\n * itself a fifth while sounding a sixth.\n *\n * @param data The plain interval.\n * @returns The wrapped interval.\n * @throws If the number, quality, and span do not describe the same interval.\n */\n static fromData(data: SpelledInterval): Interval {\n // The direction is read off the canonical data rather than from the span's\n // sign: an interval can climb a letter while losing a semitone — C# up to\n // Dbb is a doubly diminished second — and only the pitch module's reading\n // of number, quality and span together tells that from a descent. The\n // canonical form carries the flag exactly when the interval descends.\n const checked = toSpelledInterval(data);\n return new Interval(\n checked.number,\n checked.quality,\n checked.semitones,\n checked.descending ?? false,\n );\n }\n\n /**\n * Rebuild an interval from its {@link Interval.toJSON} output.\n *\n * @param data The serialized interval.\n * @returns The wrapped interval.\n * @throws If the number, quality, and span do not describe the same interval.\n */\n static fromJSON(data: SpelledInterval): Interval {\n return Interval.fromData(data);\n }\n\n /**\n * Parse an interval name such as `'P5'`, `'m3'`, `'AA4'`, or `'-m3'`.\n *\n * @param name The interval name; a leading `'-'` names the interval taken\n * downward.\n * @returns The interval of that name, descending when the name is prefixed.\n * @throws If the name is not a quality label followed by a diatonic number.\n * Use {@link Interval.tryParse} where failure is ordinary, such as an\n * interval field read on every keystroke.\n */\n static parse(name: string): Interval {\n return unwrapParse(Interval.tryParse(name));\n }\n\n /**\n * Parse an interval name, reporting failure instead of throwing it.\n *\n * The same reading as {@link Interval.parse}, for the callers where a name\n * that does not parse yet is the normal state of the input rather than a\n * fault.\n *\n * @param name The interval name.\n * @returns The interval, or the error explaining why the text is not one.\n * @example\n * ```ts\n * import { Interval } from '@libraz/libcantus';\n * const result = Interval.tryParse('M5');\n * result.ok ? result.value.name : result.error.message; // a major fifth does not exist\n * ```\n */\n static tryParse(name: string): ParseResult<Interval> {\n const parsed = tryParseInterval(name);\n return parsed.ok ? { ok: true, value: Interval.fromData(parsed.value) } : parsed;\n }\n\n /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */\n get number(): number {\n return this.#number;\n }\n\n /** Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`. */\n get quality(): IntervalQualityLabel {\n return this.#quality;\n }\n\n /** Signed semitone span. */\n get semitones(): number {\n return this.#semitones;\n }\n\n /**\n * Whether the interval moves downward. A descending unison spans zero\n * semitones, so this flag is the only record of its direction.\n */\n get isDescending(): boolean {\n return this.#descending;\n }\n\n /** A readable label composed of quality and number, e.g. `'P5'` or `'M3'`. */\n get name(): string {\n return `${this.#quality}${this.#number}`;\n }\n\n /** A copy of the underlying plain interval data. */\n get data(): SpelledInterval {\n return this.toJSON();\n }\n\n /**\n * The interval's inversion: the complement that completes the octave.\n *\n * A compound interval is reduced to its simple form first, and the result is\n * always ascending — an inversion answers \"what is left of the octave\",\n * which has no direction of its own. The reduction keeps the octave and its\n * multiples on the octave, so the answer depends on the relation between the\n * two notes and not on how many octaves apart they happen to sit: a double\n * octave inverts to a unison exactly as a single octave does.\n *\n * @returns The inverted interval, e.g. `M3` becomes `m6`.\n * @throws For an augmented octave, whose complement would be a diminished\n * unison: the letters do not move in a unison, so the pitch module reads\n * that relation as the descending `'-A1'` and refuses the name. Every other\n * interval inverts.\n */\n invert(): Interval {\n const numberValue = INVERSION_SUM - simpleDegree(this.#number);\n const quality = invertQuality(this.#quality);\n return Interval.of(numberValue, quality, intervalSemitones(numberValue, quality));\n }\n\n /**\n * The same interval taken in the opposite direction.\n *\n * The number and quality are untouched — only the direction changes, so an\n * ascending major third becomes a descending major third rather than its\n * inversion.\n *\n * @returns The interval with its direction flipped.\n * @example\n * ```ts\n * import { Interval } from '@libraz/libcantus';\n * Interval.parse('M3').negate().toString(); // 'M3', descending\n * ```\n */\n negate(): Interval {\n // The flag is flipped rather than derived from the negated span: a unison\n // spans zero semitones, whose negation is indistinguishable from itself.\n return new Interval(\n this.#number,\n this.#quality,\n this.#semitones === 0 ? 0 : -this.#semitones,\n !this.#descending,\n );\n }\n\n /**\n * Whether the interval is consonant.\n *\n * @param twoVoice When true, the perfect fourth counts as a dissonance,\n * matching two-voice counterpoint.\n * @returns True if the interval is consonant in that context.\n */\n isConsonant(twoVoice = true): boolean {\n return isConsonantInterval(this.#semitones, twoVoice);\n }\n\n /**\n * Whether another interval is spelled identically.\n *\n * Enharmonic equivalents are not equal: an augmented second and a minor\n * third span the same distance but are different intervals.\n *\n * @param other The interval to compare with.\n * @returns True when number, quality, and span all match.\n */\n equals(other: Interval): boolean {\n // The other interval is read through its public accessors rather than its\n // private fields: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n return (\n this.#number === other.number &&\n this.#quality === other.quality &&\n this.#semitones === other.semitones &&\n this.#descending === other.isDescending\n );\n }\n\n /**\n * The plain interval data, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(interval)` from collapsing to `{}`. The result is the\n * canonical shape the pitch module produces: `descending` appears only on an\n * interval that descends, so a measured interval, a parsed name, and this\n * method all serialize to the same object.\n *\n * @returns The diatonic number, quality, and semitone span, with\n * `descending` when the interval descends.\n */\n toJSON(): SpelledInterval {\n return this.#descending\n ? {\n number: this.#number,\n quality: this.#quality,\n semitones: this.#semitones,\n descending: true,\n }\n : { number: this.#number, quality: this.#quality, semitones: this.#semitones };\n }\n\n /**\n * The interval's name, so a template literal or a log line reads as the\n * interval.\n *\n * @returns The name, e.g. `'M3'`.\n */\n toString(): string {\n return this.name;\n }\n}\n","import { InvalidInputError, type ParseResult, unwrapParse } from '../core/errors/index.js';\nimport {\n formatNote,\n type IntervalLike,\n midiToNote,\n type Note as NoteData,\n type NoteNameOptions,\n noteToMidi,\n noteToPitchClass,\n type SpelledInterval,\n spelledInterval,\n toSpelledInterval,\n transposeByInterval,\n transposeNote,\n tryParseNote,\n} from '../core/pitch/index.js';\nimport { frequencyOf, type TuningTable } from '../core/tuning/index.js';\nimport { type KeyLike, pitchToScaleDegree, toKeyScale } from '../theory/scale/index.js';\nimport { type TransposingInstrument, toWrittenPitch } from '../theory/transposition/index.js';\nimport { Interval } from './interval.js';\n\n/**\n * Defensive copy of a plain note, with the letter checked.\n *\n * Formatting and pitch-class arithmetic reduce the letter modulo 7, but\n * `equals` compares it directly: an unreduced letter produces two notes that\n * print the same name and report the same pitch class yet compare unequal.\n */\nfunction copyNote(data: NoteData): NoteData {\n if (!Number.isInteger(data.letter) || data.letter < 0 || data.letter > 6) {\n throw new InvalidInputError(\n `note letter must be an integer in [0, 6]; received ${data.letter}`,\n );\n }\n const copy: NoteData = { letter: data.letter, alter: data.alter };\n if (data.octave !== undefined) {\n copy.octave = data.octave;\n }\n // Delegate alteration and octave bounds to the same guard used by the core\n // pitch functions. The class must not accept data its own methods reject.\n noteToPitchClass(copy);\n return copy;\n}\n\n/** A bare natural letter name, the only text {@link Note.of} reads. */\nconst NATURAL_LETTER = /^[A-G]$/;\n\n/**\n * The letter number a `Note.of` letter argument names.\n *\n * A letter name is read through the note parser rather than a table of its\n * own, so the letter ordering has one definition. Anything the parser would\n * have to interpret — an accidental, an octave, a non-English glyph — is\n * refused here instead, because `of` builds from parts and `parse` reads text.\n */\nfunction letterNumberOf(letter: number | string): number {\n if (typeof letter === 'number') {\n // Left to copyNote, which rejects the same out-of-range letters a plain\n // note object arriving from outside is rejected for.\n return letter;\n }\n if (!NATURAL_LETTER.test(letter)) {\n throw new InvalidInputError(\n `Note.of takes a letter number 0..6 or a bare letter name 'C'..'B'; received ${JSON.stringify(letter)}. Use Note.parse(${JSON.stringify(letter)}) to read a note name with an accidental or an octave.`,\n );\n }\n return unwrapParse(tryParseNote(letter)).letter;\n}\n\n/** Widest alteration an enharmonic spelling may carry: a double accidental. */\nconst DOUBLE_ACCIDENTAL = 2;\n\n/** The zero-semitone second onto the next letter up, which respells a note. */\nconst DIMINISHED_SECOND: SpelledInterval = { number: 2, quality: 'd', semitones: 0 };\n\n/** The same second taken downwards, onto the letter below. */\nconst DESCENDING_DIMINISHED_SECOND: SpelledInterval = {\n ...DIMINISHED_SECOND,\n descending: true,\n};\n\n/**\n * The number a note is ordered by.\n *\n * The MIDI number where there is an octave to place the note in, and the bare\n * pitch class where there is not — one number per note either way, so a list\n * sorts consistently however its notes were written.\n */\nfunction soundingOrder(note: NoteData): number {\n return note.octave === undefined ? noteToPitchClass(note) : noteToMidi(note);\n}\n\n/**\n * An immutable spelled note: a diatonic letter plus a chromatic alteration and\n * an optional octave. Wraps the plain note object and delegates to the pitch\n * module; every transformation returns a new instance.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('C4').transpose(7).name; // 'G4'\n * ```\n */\nexport class Note {\n readonly #data: NoteData;\n\n /**\n * Wrap a plain note object.\n *\n * @param data The spelled note; it is copied, never retained or mutated.\n * @throws If `letter` is not an integer in 0..6.\n */\n constructor(data: NoteData) {\n this.#data = copyNote(data);\n }\n\n /**\n * Build a note from its parts, without reading any text.\n *\n * The parts are the fields a note holds, so nothing here is interpreted:\n * {@link Note.parse} is what reads `'Bb'` or `'C#4'`, and a letter name given\n * here may carry neither an accidental nor an octave.\n *\n * @param letter The diatonic letter, as a number 0..6 (C..B) or a bare\n * natural letter name `'C'`..`'B'`.\n * @param alter The chromatic alteration in semitones; defaults to natural.\n * @param octave The octave (scientific pitch notation); omit it for a bare\n * pitch class.\n * @returns The note.\n * @throws If the letter is out of range, if a letter name carries an\n * accidental or an octave, or if the alteration or octave is out of range.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.of(1, -1, 4).name; // 'Db4'\n * Note.parse('G').name; // 'G'\n * ```\n */\n static of(letter: number | string, alter = 0, octave?: number): Note {\n const data: NoteData = { letter: letterNumberOf(letter), alter };\n if (octave !== undefined) {\n data.octave = octave;\n }\n return new Note(data);\n }\n\n /**\n * Parse a note name (e.g. `'C#4'`, `'Bb'`, `'F##3'`), in any of the\n * supported note-name systems.\n *\n * The system is detected from the name itself unless one is given, exactly as\n * {@link Key.parse} reads a key name: `'gis'` is a G sharp, while a bare\n * `'B'` is the English B natural until `'german'` says otherwise.\n *\n * @param text The note text.\n * @param opts `system` reads the name in that notation system instead of\n * detecting it.\n * @returns The parsed note.\n * @throws If the text is not a valid note. Use {@link Note.tryParse} where\n * failure is ordinary, such as a note field read on every keystroke.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('gis').name; // 'G#'\n * Note.parse('B', { system: 'german' }).name; // 'Bb'\n * ```\n */\n static parse(text: string, opts?: NoteNameOptions): Note {\n return unwrapParse(Note.tryParse(text, opts));\n }\n\n /**\n * Parse a note name, reporting failure instead of throwing it.\n *\n * The same reading as {@link Note.parse}, for the callers where text that\n * does not name a note yet is the normal state of the input rather than a\n * fault.\n *\n * @param name The note text.\n * @param opts `system` reads the name in that notation system instead of\n * detecting it.\n * @returns The note, or the error explaining why the text is not one.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * const result = Note.tryParse('C#4');\n * result.ok ? result.value.midi : result.error.message; // 61\n * ```\n */\n static tryParse(name: string, opts?: NoteNameOptions): ParseResult<Note> {\n const parsed = tryParseNote(name, opts);\n return parsed.ok ? { ok: true, value: new Note(parsed.value) } : parsed;\n }\n\n /**\n * Name a MIDI number (middle C = C4 = 60) as a note.\n *\n * @param midi The MIDI number.\n * @param spelling Whether to prefer sharps or flats for black keys.\n * @returns The spelled note, with octave.\n */\n static fromMidi(midi: number, spelling: 'sharp' | 'flat' = 'sharp'): Note {\n return new Note(midiToNote(midi, spelling));\n }\n\n /**\n * Wrap an existing plain note object.\n *\n * @param data The plain note.\n * @returns The wrapped note.\n * @throws If `letter` is not an integer in 0..6.\n */\n static fromData(data: NoteData): Note {\n return new Note(data);\n }\n\n /**\n * The note rendered as scientific pitch notation in English, e.g. `'G4'` or\n * `'Bb'`. Use {@link Note.format} to write it in another notation system.\n */\n get name(): string {\n return formatNote(this.#data);\n }\n\n /**\n * The note name written in a notation system.\n *\n * The counterpart of {@link Note.parse}: a getter cannot take an argument, so\n * the system is named here instead of on {@link Note.name}.\n *\n * @param opts `system` writes the name in that notation system instead of\n * English.\n * @returns The note name, including the octave when the note has one.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('G#').format({ system: 'german' }); // 'gis'\n * Note.parse('G#').format({ system: 'japanese' }); // '嬰ト'\n * ```\n */\n format(opts?: NoteNameOptions): string {\n return formatNote(this.#data, opts);\n }\n\n /** The pitch class (0..11), ignoring octave. */\n get pitchClass(): number {\n return noteToPitchClass(this.#data);\n }\n\n /**\n * The MIDI number (middle C = C4 = 60).\n *\n * @throws If the note has no octave and therefore no fixed pitch.\n */\n get midi(): number {\n if (this.#data.octave === undefined) {\n throw new InvalidInputError(`note ${this.name} has no octave, so it has no MIDI number`);\n }\n return noteToMidi(this.#data);\n }\n\n /** The diatonic letter number: 0..6 for C..B. */\n get letter(): number {\n return this.#data.letter;\n }\n\n /** The chromatic alteration in semitones: -1 flat, 0 natural, +1 sharp, ... */\n get alter(): number {\n return this.#data.alter;\n }\n\n /** The octave (scientific pitch notation), or undefined for a bare pitch class. */\n get octave(): number | undefined {\n return this.#data.octave;\n }\n\n /** A copy of the underlying plain note object. */\n get data(): NoteData {\n return copyNote(this.#data);\n }\n\n /**\n * The note's frequency in Hz.\n *\n * The note's MIDI number is read as a step index, which is how the tuning\n * module numbers steps: under twelve divisions the two coincide, and under\n * any other the step index keeps counting from the same reference. A\n * {@link Tuning} carries the three fields this takes, so an instance of it\n * can be passed here directly.\n *\n * @param tuning The temperament to read the note under; twelve-tone equal\n * temperament with A4 = 440 Hz by default.\n * @returns The frequency in Hz.\n * @throws If the note has no octave and therefore no fixed pitch, if the\n * temperament describes no tuning, or if the frequency falls outside the\n * range a number holds.\n * @example\n * ```ts\n * import { Note, Tuning } from '@libraz/libcantus';\n * Note.parse('A4').frequency(); // 440\n * Math.round(Note.parse('C4').frequency()); // 262\n * Note.parse('A4').frequency(Tuning.edo(19)); // 440, the reference step of any EDO\n * ```\n */\n frequency(tuning?: TuningTable): number {\n return frequencyOf(this.midi, tuning);\n }\n\n /**\n * Transpose by a signed number of semitones, keeping the spelling.\n *\n * The letter moves by the diatonic distance of the conventional interval for\n * that many semitones, so `Ab4` up a major second is `Bb4` rather than `A#4`\n * and a flat key stays on the flat side. An octave-less note stays\n * octave-less: only its pitch class is moved. Transposing by zero is the\n * identity: the original spelling is preserved (no enharmonic respelling).\n *\n * @param semitones The signed semitone offset.\n * @param opts `spelling` forces the result onto the sharp or flat side\n * instead of following this note's letter.\n * @returns The transposed note.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('Ab4').transpose(2).name; // 'Bb4'\n * Note.parse('Ab4').transpose(2, { spelling: 'sharp' }).name; // 'A#4'\n * ```\n */\n transpose(semitones: number, opts?: { spelling?: 'sharp' | 'flat' }): Note {\n if (semitones === 0 && opts?.spelling === undefined) {\n return new Note(this.#data);\n }\n return new Note(transposeNote(this.#data, semitones, opts));\n }\n\n /**\n * The spelled interval from this note to another.\n *\n * @param other The second note.\n * @returns The interval, which {@link Interval.toJSON} unwraps to plain data.\n */\n intervalTo(other: Note): Interval {\n // Read the other note through its public accessor rather than its private\n // field: a bundler that emits two copies of this class — as a CommonJS\n // build without shared chunks does for the root and /model entries — would\n // otherwise throw on the brand check.\n return Interval.fromData(spelledInterval(this.#data, other.data));\n }\n\n /**\n * Transpose by a spelled interval, keeping the spelling the interval names.\n *\n * Unlike {@link Note.transpose}, which picks a letter from the semitone\n * count, the interval's diatonic number decides the letter: C up an\n * augmented second is D#, not Eb.\n *\n * @param interval An interval name (e.g. `'A2'`, `'-m3'`), plain interval\n * data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed note.\n * @example\n * ```ts\n * import { Interval, Note } from '@libraz/libcantus';\n * Note.parse('C4').transposeBy('A2').name; // 'D#4'\n * Note.parse('C4').transposeBy(Interval.parse('A2')).name; // 'D#4'\n * Note.parse('C4').transposeBy('A4').name; // 'F#4', not 'Gb4'\n * ```\n */\n transposeBy(interval: IntervalLike): Note {\n return new Note(transposeByInterval(this.#data, toSpelledInterval(interval)));\n }\n\n /**\n * The same spelled note in another octave.\n *\n * The letter and the alteration are untouched, so this places a note in a\n * register rather than moving it by an interval: `Cb4` given octave 3 is\n * `Cb3`, not the `B3` it sounds as.\n *\n * @param octave The octave (scientific pitch notation), where middle C is C4.\n * @returns The note in that octave.\n * @throws If the octave is not an integer in -100..100.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('C#').withOctave(4).name; // 'C#4'\n * Note.parse('Eb5').withOctave(3).name; // 'Eb3'\n * ```\n */\n withOctave(octave: number): Note {\n return new Note({ letter: this.#data.letter, alter: this.#data.alter, octave });\n }\n\n /**\n * The note a transposing instrument must read to sound this one.\n *\n * The direction is written-side, as {@link Key.forInstrument} is for a key:\n * the part is transposed *away* from what the instrument sounds, so a B flat\n * instrument — which sounds a major second lower than it reads — has a\n * concert C4 written as D4. The interval decides the letter, so a concert\n * E flat 3 reads as C4 on an alto saxophone rather than as B sharp 3.\n *\n * The opposite reading, a written note back to the pitch it sounds, is\n * {@link Note.transposeBy} applied to {@link instrumentTransposition} — the\n * instrument's own written-to-sounding interval.\n *\n * @param instrument A built-in instrument name, or an interval naming a\n * transposition the table does not carry.\n * @returns The note the player reads.\n * @throws If the instrument is neither a known name nor a spelled interval.\n * @example\n * ```ts\n * import { instrumentTransposition, Note } from '@libraz/libcantus';\n * Note.parse('C4').forInstrument('clarinetBb').name; // 'D4'\n * Note.parse('Eb3').forInstrument('altoSax').name; // 'C4'\n * // And back: what a written C4 on a clarinet in A sounds as.\n * Note.parse('C4').transposeBy(instrumentTransposition('clarinetA')).name; // 'A3'\n * ```\n */\n forInstrument(instrument: TransposingInstrument): Note {\n // The same conversion the key uses, so a part and its key signature can\n // never disagree about the direction or the spelling.\n return new Note(toWrittenPitch(this.#data, instrument));\n }\n\n /**\n * The other ways this note can be spelled: the same sounding pitch written on\n * the letter above and on the letter below.\n *\n * A list rather than a single answer, because a note has no one enharmonic\n * partner the way a key has: `C#4` is both `Db4` and `B##3`. Only spellings\n * within a double accidental are offered — the letters two steps away would\n * need a triple one — so a note whose neighbours are unwritable gets a\n * shorter list, and an octave-less note stays octave-less.\n *\n * @returns The alternative spellings, the letter above first.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('C#4').enharmonic().map((note) => note.name); // ['Db4', 'B##3']\n * Note.parse('Cb4').enharmonic().map((note) => note.name); // ['B3']\n * ```\n */\n enharmonic(): Note[] {\n return [DIMINISHED_SECOND, DESCENDING_DIMINISHED_SECOND]\n .map((interval) => transposeByInterval(this.#data, interval))\n .filter((note) => Math.abs(note.alter) <= DOUBLE_ACCIDENTAL)\n .map((note) => new Note(note));\n }\n\n /**\n * The scale degree this note occupies in a key, counted from 1 at the tonic.\n *\n * The degree is read from the sounding pitch, so an enharmonic spelling\n * answers for the pitch it sounds: in C major both `F#` and `Gb` are outside\n * the scale, and both `B#` and `C` are the tonic.\n *\n * @param key The key to measure against; a key name, a plain key/scale, or a\n * {@link Key}.\n * @returns The 1-based degree, or null when the note is not in the scale.\n * @throws If the value names no key.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('E4').degreeIn('C major'); // 3\n * Note.parse('F#4').degreeIn('C major'); // null\n * ```\n */\n degreeIn(key: KeyLike): number | null {\n // The scale function reports -1 for a pitch it does not contain; the class\n // API says \"no answer\" with null, as `Key.enharmonic` and `Chord.detectBest`\n // do, so a caller cannot read the miss as a degree.\n const degree = pitchToScaleDegree(this.pitchClass, toKeyScale(key));\n return degree === -1 ? null : degree;\n }\n\n /**\n * Rebuild a note from its {@link Note.toJSON} output.\n *\n * @param data The serialized note.\n * @returns The wrapped note.\n * @throws If `letter` is not an integer in 0..6.\n */\n static fromJSON(data: NoteData): Note {\n return new Note(data);\n }\n\n /**\n * Order two notes by the pitch they sound, for `Array.prototype.sort`.\n *\n * Sounding pitch, not spelling: an enharmonic pair compares equal, so `C#4`\n * and `Db4` keep the order they were given in while every note that sounds\n * lower comes before them. {@link Note.equals} is the spelling comparison,\n * and the two deliberately disagree about enharmonics.\n *\n * A note carrying an octave is ordered by its MIDI number and an octave-less\n * one by its pitch class, so the octave-less notes of a mixed list gather at\n * the bottom rather than being placed in a register they do not name.\n *\n * @param other The note to compare with.\n * @returns Negative when this note sounds lower, zero when the two sound the\n * same pitch, positive when it sounds higher.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * const notes = [Note.parse('G4'), Note.parse('C4'), Note.parse('E4')];\n * notes.sort((a, b) => a.compareTo(b)).map((note) => note.name); // ['C4', 'E4', 'G4']\n * Note.parse('C#4').compareTo(Note.parse('Db4')); // 0\n * ```\n */\n compareTo(other: Note): number {\n // The other note is read through its public accessor rather than its\n // private field, as `equals` is, so two copies of the class still compare.\n return soundingOrder(this.#data) - soundingOrder(other.data);\n }\n\n /**\n * Whether another note has the same letter, alteration, and octave.\n *\n * @param other The note to compare.\n * @returns True if the spellings are identical.\n */\n equals(other: Note): boolean {\n const b = other.data;\n return (\n this.#data.letter === b.letter &&\n this.#data.alter === b.alter &&\n this.#data.octave === b.octave\n );\n }\n\n /**\n * The plain note data, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(note)` from collapsing to `{}`.\n *\n * @returns A copy of the underlying plain note object.\n */\n toJSON(): NoteData {\n return this.data;\n }\n\n /**\n * The note's name, so a template literal or a log line reads as the note.\n *\n * @param opts `system` writes the name in that notation system instead of\n * English, as {@link Key.toString} does for a key.\n * @returns The spelled name, e.g. `'Bb3'`.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * Note.parse('G#4').toString({ system: 'japanese' }); // '嬰ト4'\n * ```\n */\n toString(opts?: NoteNameOptions): string {\n return this.format(opts);\n }\n}\n","import { InvalidInputError } from '../core/errors/index.js';\nimport type { TimeSignature } from '../core/meter/index.js';\nimport { midiToNote, pitchClassOf as mod12, type Note as NoteData } from '../core/pitch/index.js';\nimport type { NoteEvent } from '../core/types.js';\nimport { assertTimeSignature } from '../core/validation/index.js';\n\nexport { pitchClassOf as mod12 } from '../core/pitch/index.js';\n\n/**\n * Refuse anything but a key where a method takes the key first.\n *\n * These methods read a key and then their options, so an options bag passed\n * on its own is taken for the key. Nothing notices until a `scale` is read off\n * it several calls later, and the error names a field of a key the caller\n * never wrote — `modeMask12 must be finite; received undefined` for an object\n * whose only property is `alternatives`. The shape is checked here instead,\n * where the mistake is still the caller's own argument.\n *\n * A key is recognised by carrying a scale rather than by its type, the way\n * every other boundary in the library reads one, so a `Key` built by a second\n * copy of the module passes.\n */\nexport function assertKeyArgument(value: unknown, name: string): void {\n if (value === undefined || (typeof value === 'object' && value !== null && 'scale' in value)) {\n return;\n }\n const received = typeof value === 'object' && value !== null ? 'an object' : `a ${typeof value}`;\n throw new InvalidInputError(\n `${name} must be a Key; received ${received}. A method that also takes options takes them after the key, not in its place`,\n );\n}\n\n/** Spell a bare pitch class as an octave-less note with a sharp/flat preference. */\nexport function spellPitchClassBare(pc: number, spelling: 'sharp' | 'flat'): NoteData {\n const spelled = midiToNote(60 + mod12(pc), spelling);\n return { letter: spelled.letter, alter: spelled.alter };\n}\n\n/** Zero with its sign dropped, so `-0` never reaches the plain data. */\nexport function withoutNegativeZero(value: number): number {\n return value === 0 ? 0 : value;\n}\n\n/** Where the last of a run of spans ends, and never before beat 0. */\nexport function spanEnd(...runs: readonly (readonly { endBeat: number }[])[]): number {\n let end = 0;\n for (const run of runs) {\n for (const span of run) {\n end = Math.max(end, span.endBeat);\n }\n }\n return end;\n}\n\n/**\n * Defensive copy of one note event, carrying only the fields a note holds.\n *\n * A `-0` onset survives every arithmetic path that produced it but not\n * `JSON.stringify`, so it is dropped here rather than becoming a value that\n * compares unequal to its own serialization.\n *\n * The event is copied as given: the classes that check their input do so\n * before copying, since what counts as an acceptable note differs between\n * written material and an imported track.\n */\nexport function copyNoteEvent(note: NoteEvent): NoteEvent {\n const copy: NoteEvent = {\n pitch: withoutNegativeZero(note.pitch),\n startBeat: withoutNegativeZero(note.startBeat),\n durationBeat: withoutNegativeZero(note.durationBeat),\n };\n if (note.velocity !== undefined) {\n copy.velocity = withoutNegativeZero(note.velocity);\n }\n if (note.articulation !== undefined) {\n copy.articulation = note.articulation;\n }\n return copy;\n}\n\n/**\n * A defensive copy of a plain time signature, checked the way every meter\n * function checks it.\n *\n * Data arriving from a project file, a plugin, or a hand-edited JSON enters\n * here, so it is held to the rules a parsed signature is held to: an unchecked\n * numerator of zero or of `NaN` would divide every bar that reads it.\n *\n * @param name - What the signature is called in an error message.\n */\nexport function copyTimeSignature(ts: TimeSignature, name?: string): TimeSignature {\n assertTimeSignature(ts, name);\n const copy: TimeSignature = { numerator: ts.numerator, denominator: ts.denominator };\n if (ts.grouping !== undefined) {\n copy.grouping = [...ts.grouping];\n }\n return copy;\n}\n\n/**\n * Whether two plain values hold the same data, key order aside.\n *\n * Records, arrays and primitives are all a model class hands out, so one walk\n * answers `equals` for every one of them rather than each class comparing the\n * shapes its own data happens to carry.\n */\nexport function samePlain(value: unknown, other: unknown): boolean {\n if (Array.isArray(value) || Array.isArray(other)) {\n return (\n Array.isArray(value) &&\n Array.isArray(other) &&\n value.length === other.length &&\n value.every((item, index) => samePlain(item, other[index]))\n );\n }\n if (typeof value === 'object' && value !== null && typeof other === 'object' && other !== null) {\n const mine = Object.entries(value);\n const theirs = other as Record<string, unknown>;\n return (\n mine.length === Object.keys(theirs).length &&\n mine.every(([key, item]) => key in theirs && samePlain(item, theirs[key]))\n );\n }\n return value === other;\n}\n","import type { ChordToRomanOptions } from '../analyze/functional/index.js';\nimport { chordToRoman } from '../analyze/functional/index.js';\nimport type { KeyRegion } from '../analyze/keys/index.js';\nimport { keyLookup, prevailingKeyOf } from '../analyze/keys/index.js';\nimport type { ReducedChord, ReduceProgressionOptions } from '../analyze/reduction/index.js';\nimport { reduceProgression } from '../analyze/reduction/index.js';\nimport type { CadenceHit, ChordTimeline, ChordTimelineOptions } from '../analyze/timeline/index.js';\nimport {\n chordTimelineFromChords,\n chordTimelineFromNotes,\n detectCadences,\n} from '../analyze/timeline/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { IntervalLike } from '../core/pitch/index.js';\nimport type { KeyScale, NoteEvent } from '../core/types.js';\nimport { assertFiniteNumber, assertRange } from '../core/validation/index.js';\nimport type { Chord as ChordData, ChordSegment, ChordSpan } from '../theory/chord/index.js';\nimport { spanFromChord } from '../theory/chord/index.js';\nimport type { KeyLike } from '../theory/scale/index.js';\nimport { toKeyScale } from '../theory/scale/index.js';\nimport type { Chord } from './chord.js';\nimport { Chord as ChordClass } from './chord.js';\nimport type { Key } from './key.js';\nimport { Key as KeyClass } from './key.js';\nimport { Progression } from './progression.js';\nimport { spanEnd } from './shared.js';\n\n/** The plain form a {@link Timeline} hands out and is rebuilt from. */\nexport type TimelineData = {\n segments: ChordSegment[];\n totalBeats: number;\n keys?: KeyRegion[];\n /**\n * How sure the analysis is of each segment's chord, in [0, 1] and in segment\n * order. Carried only by a timeline read from notes: chords a caller placed\n * are not a reading, so a timeline built from them has nothing to report.\n */\n segmentConfidence?: number[];\n};\n\n/**\n * One segment's Roman numeral, with the beats the chord holds it for.\n *\n * A numeral on its own says nothing about when it sounds, and a timed class\n * that answered with a bare list would leave the caller re-deriving the onsets\n * it already holds. The beat range is the segment's own, so a numeral list and\n * a segment list read in step — the shape {@link ReducedChord} already uses.\n */\nexport type TimelineRoman = {\n startBeat: number;\n endBeat: number;\n roman: string;\n};\n\n/**\n * The confidence a key region carries when the caller named the key.\n *\n * A stated key is not a measurement, and reporting anything less would make a\n * caller's own answer look like a doubtful reading of the notes.\n */\nconst GIVEN_KEY_CONFIDENCE = 1;\n\n/** A validated, defensive copy of one chord segment. */\nfunction copySegment(segment: ChordSegment): ChordSegment {\n return {\n startBeat: assertFiniteNumber(segment.startBeat, 'timeline segment startBeat'),\n endBeat: assertFiniteNumber(segment.endBeat, 'timeline segment endBeat'),\n // The chord is checked by the same boundary every other chord crosses, so a\n // root of 25 or an interval of NaN is refused or reduced here rather than\n // surfacing wherever the number is finally used.\n chord: new ChordClass(segment.chord).toJSON(),\n };\n}\n\n/** A validated, defensive copy of one key region, pivot chord included. */\nfunction copyKeyRegion(region: KeyRegion): KeyRegion {\n const copy: KeyRegion = {\n startBeat: assertFiniteNumber(region.startBeat, 'key region startBeat'),\n endBeat: assertFiniteNumber(region.endBeat, 'key region endBeat'),\n key: toKeyScale(region.key),\n confidence: assertFiniteNumber(region.confidence, 'key region confidence'),\n };\n if (region.modulation !== undefined) {\n copy.modulation = region.modulation;\n }\n if (region.pivot !== undefined) {\n copy.pivot = {\n chord: new ChordClass(region.pivot.chord).toJSON(),\n romanFrom: region.pivot.romanFrom,\n romanTo: region.pivot.romanTo,\n };\n }\n return copy;\n}\n\n/**\n * A validated copy of the per-segment confidences, one per segment.\n *\n * The list is meaningless unless it lines up with the segments — it carries no\n * beats of its own and is read by position — so a list of another length is\n * refused rather than silently paired off against the segments it happens to\n * cover.\n */\nfunction copyConfidence(\n confidence: readonly number[] | undefined,\n segments: number,\n): readonly number[] {\n if (confidence === undefined) {\n return [];\n }\n if (confidence.length !== segments) {\n throw new InvalidInputError(\n `timeline segmentConfidence must hold one value per segment; received ${confidence.length} for ${segments}`,\n );\n }\n return confidence.map((value, index) =>\n assertRange(value, 0, 1, `timeline segmentConfidence[${index}]`),\n );\n}\n\n/**\n * The chord sounding at a beat, or null where nothing is.\n *\n * Segments are disjoint and in beat order, so the covering one is found by\n * binary search rather than by scanning: this is what `at(beat)` and every\n * analysis reading the timeline go through.\n */\nfunction chordAtBeat(segments: readonly ChordSegment[], beat: number): ChordData | null {\n assertFiniteNumber(beat, 'timeline query beat');\n let low = 0;\n let high = segments.length;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n if ((segments[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n const candidate = segments[low - 1];\n return candidate !== undefined && beat >= candidate.startBeat && beat < candidate.endBeat\n ? candidate.chord\n : null;\n}\n\n/** The part of a span lying inside `[fromBeat, toBeat)`, or null when none does. */\nfunction clipSpan<T extends { startBeat: number; endBeat: number }>(\n span: T,\n fromBeat: number,\n toBeat: number,\n): T | null {\n const startBeat = Math.max(span.startBeat, fromBeat);\n const endBeat = Math.min(span.endBeat, toBeat);\n return endBeat > startBeat ? { ...span, startBeat, endBeat } : null;\n}\n\n/**\n * Harmony that keeps its place in time: chord segments over a span of beats,\n * with the key regions the analysis found under them.\n *\n * This is the timed counterpart of {@link Progression}, which holds an ordered\n * list of chords and deliberately drops their onsets. A piece read from a MIDI\n * track has both a chord order and a chord rhythm, and until now only the order\n * could be held in a class; `Timeline` is where the rhythm lives.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Chord, Key, Progression, Timeline } from '@libraz/libcantus';\n * const timeline = Timeline.fromProgression(\n * new Progression([Chord.parse('C'), Chord.parse('G7')], Key.major('C')),\n * 4,\n * );\n * timeline.at(5)?.symbol(); // 'G7'\n * timeline.roman().map((entry) => entry.roman); // ['I', 'V7']\n * ```\n */\nexport class Timeline {\n readonly #segments: readonly ChordSegment[];\n readonly #totalBeats: number;\n readonly #keys: readonly KeyRegion[];\n readonly #segmentConfidence: readonly number[];\n\n /**\n * Wrap plain timeline data.\n *\n * @param data The segments and the span they cover; copied, never retained.\n * @throws If the span, a segment, or a key region carries a number it cannot\n * hold — a beat that is not finite, a mode mask that names no scale — or if\n * the confidences do not run one per segment.\n */\n constructor(data: TimelineData) {\n this.#segments = Object.freeze(data.segments.map(copySegment));\n this.#totalBeats = assertRange(\n data.totalBeats,\n 0,\n Number.MAX_SAFE_INTEGER,\n 'timeline totalBeats',\n );\n this.#keys = Object.freeze((data.keys ?? []).map(copyKeyRegion));\n this.#segmentConfidence = Object.freeze(\n copyConfidence(data.segmentConfidence, this.#segments.length),\n );\n }\n\n /**\n * Build a timeline from placed chords.\n *\n * @param spans The chords with their onsets, in any order.\n * @param totalBeats Where the last chord stops sounding.\n * @param key The key the chords are read in, when it is already known. It\n * becomes the one key region under the whole span; without it the timeline\n * carries no key, and the members that need one say so.\n * @returns The timeline.\n */\n static fromChords(spans: readonly ChordSpan[], totalBeats: number, key?: KeyLike): Timeline {\n const timeline = chordTimelineFromChords(spans, totalBeats);\n const data: TimelineData = { segments: timeline.segments, totalBeats };\n if (key !== undefined) {\n data.keys = [\n {\n // The region starts where the music does, as an inferred one does,\n // so a chord placed in a pickup is covered by the key it sounds in.\n startBeat: timeline.segments[0]?.startBeat ?? 0,\n endBeat: totalBeats,\n key: toKeyScale(key),\n confidence: GIVEN_KEY_CONFIDENCE,\n },\n ];\n }\n return new Timeline(data);\n }\n\n /**\n * Infer a timeline from sounding notes.\n *\n * @param notes The note events to read.\n * @param opts The options {@link chordTimelineFromNotes} accepts.\n * @returns The inferred timeline, carrying the key regions the analysis found.\n */\n static fromNotes(notes: readonly NoteEvent[], opts?: ChordTimelineOptions): Timeline {\n const { timeline, keys, segmentConfidence } = chordTimelineFromNotes(notes, opts);\n // The analysis reports where the chords and the keys end rather than the\n // span it ran over, so the span is read back off them unless it was given.\n const totalBeats = opts?.totalBeats ?? spanEnd(timeline.segments, keys);\n return new Timeline({ segments: timeline.segments, totalBeats, keys, segmentConfidence });\n }\n\n /**\n * Place a progression's chords on a regular grid.\n *\n * The chords cross over as harmony alone: a spelling one of them carried is\n * dropped, and a carried key becomes the plain key/scale of the one region\n * under the span, without its spelled tonic or its detected scale form.\n *\n * @param progression The chords, in order.\n * @param beatsEach How long each chord sounds.\n * @returns The timeline.\n * @throws If `beatsEach` is not a positive finite number of beats.\n */\n static fromProgression(progression: Progression, beatsEach: number): Timeline {\n assertRange(beatsEach, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'timeline beatsEach');\n const chords = progression.chords;\n const spans = chords.map((chord, index) => spanFromChord(chord.data, index * beatsEach));\n const totalBeats = chords.length * beatsEach;\n const key = progression.key;\n return key === undefined\n ? Timeline.fromChords(spans, totalBeats)\n : Timeline.fromChords(spans, totalBeats, key);\n }\n\n /** Rebuild a timeline from the plain data {@link Timeline.data} hands out. */\n static fromData(data: TimelineData): Timeline {\n return new Timeline(data);\n }\n\n /** Rebuild a timeline from its {@link Timeline.toJSON} output. */\n static fromJSON(data: TimelineData): Timeline {\n return new Timeline(data);\n }\n\n /**\n * The chord sounding at a beat, or null where nothing is.\n *\n * A segment covers `[startBeat, endBeat)`, so the beat a chord gives way on\n * answers with the chord arriving rather than the one leaving. The chord\n * carries the key in force at that beat when the timeline knows one, so it\n * can name its own numeral and function.\n *\n * @param beat The beat to read.\n * @returns The chord, or null outside every segment.\n * @throws If `beat` is not finite.\n */\n at(beat: number): Chord | null {\n const chord = chordAtBeat(this.#segments, beat);\n if (chord === null) {\n return null;\n }\n const key = this.#keyAt(beat);\n return key === undefined ? new ChordClass(chord) : new ChordClass(chord, key);\n }\n\n /** The chord segments, in time order. */\n get segments(): readonly ChordSegment[] {\n return this.#copySegments();\n }\n\n /** Where the last chord stops sounding. */\n get totalBeats(): number {\n return this.#totalBeats;\n }\n\n /** The key held longest across the span, when the analysis found one. */\n get key(): Key | undefined {\n const prevailing = prevailingKeyOf(this.#keys);\n return prevailing === null ? undefined : KeyClass.of(prevailing);\n }\n\n /** Every key region under the span, in time order. */\n get keys(): readonly KeyRegion[] {\n return this.#keys.map(copyKeyRegion);\n }\n\n /**\n * How sure the analysis is of each segment's chord, in [0, 1] and in segment\n * order, so a reading is paired with the segment it describes by position.\n *\n * A confidence answers the question a chord symbol cannot: whether the notes\n * really spell that chord, or whether a passing figure was the best of a bad\n * set of candidates. Empty on a timeline whose chords were placed rather than\n * inferred — {@link Timeline.fromChords} and {@link Timeline.fromProgression}\n * are given the harmony, so there is no reading to report.\n *\n * @example\n * ```ts\n * import { Timeline } from '@libraz/libcantus';\n * const timeline = Timeline.fromNotes([\n * { pitch: 60, startBeat: 0, durationBeat: 4 },\n * { pitch: 64, startBeat: 0, durationBeat: 4 },\n * { pitch: 67, startBeat: 0, durationBeat: 4 },\n * ]);\n * timeline.segmentConfidence.length === timeline.length; // true\n * ```\n */\n get segmentConfidence(): readonly number[] {\n return [...this.#segmentConfidence];\n }\n\n /** How many segments the timeline holds. */\n get length(): number {\n return this.#segments.length;\n }\n\n /** Iterate the segments in time order, so a timeline works with `for...of`. */\n [Symbol.iterator](): IterableIterator<ChordSegment> {\n return this.#copySegments()[Symbol.iterator]();\n }\n\n /**\n * Drop the time axis, keeping the chord order.\n *\n * Explicit rather than implicit, because the onsets are information and\n * losing them silently is how a chord rhythm disappears from a pipeline. A\n * rest between two segments goes with them: the chords either side become\n * neighbours in the progression.\n *\n * @returns The chords in order, carrying the prevailing key when there is one.\n */\n progression(): Progression {\n const chords = this.#segments.map((segment) => new ChordClass(segment.chord));\n const key = this.key;\n return key === undefined ? new Progression(chords) : new Progression(chords, key);\n }\n\n /**\n * The structural chords behind the surface harmony.\n *\n * @param opts Which reading decides the frame; see\n * {@link ReduceProgressionOptions}.\n * @returns One labelled chord per segment, in time order.\n * @throws If the timeline carries no key region to read the chords against.\n */\n reduce(opts?: ReduceProgressionOptions): ReducedChord[] {\n return reduceProgression(this.chordTimeline, this.#keyContext(), opts);\n }\n\n /**\n * The cadences the harmony arrives at.\n *\n * Each hit carries the beat it arrives on as well as the cadence itself: a\n * cadence without its onset says that the music cadenced but not where, which\n * is the one thing a timed class is holding that a chord pair is not.\n *\n * @returns The cadences found, in time order.\n * @throws If the timeline carries no key region to read the chords against.\n */\n cadences(): CadenceHit[] {\n return detectCadences(this.chordTimeline, this.#keyContext());\n }\n\n /**\n * The chords as roman numerals in a key.\n *\n * @param key Key to read the chords in; falls back to the key in force at\n * each segment, so a timeline that modulates names every chord in the key\n * it actually sounds in.\n * @param opts Applied-numeral rendering options; see\n * {@link ChordToRomanOptions}.\n * @returns One numeral per segment, with the beats it holds for.\n * @throws If no key is given and the timeline carries none.\n */\n roman(key?: KeyLike, opts?: ChordToRomanOptions): TimelineRoman[] {\n const given = key === undefined ? undefined : toKeyScale(key);\n const keyAt = given === undefined ? this.#keyContext() : () => given;\n return this.#segments.map((segment) => ({\n startBeat: segment.startBeat,\n endBeat: segment.endBeat,\n roman: chordToRoman(segment.chord, keyAt(segment.startBeat), opts),\n }));\n }\n\n /**\n * The stretch of the timeline between two beats.\n *\n * Beats are kept as they are rather than rebased on the slice, so a sliced\n * timeline still lines up with the score it was read from; `totalBeats`\n * becomes where the slice ends. A segment or key region straddling an edge is\n * clipped to it, and one reduced to nothing is dropped.\n *\n * @param fromBeat First beat of the stretch.\n * @param toBeat End of the stretch, exclusive.\n * @returns The clipped timeline.\n * @throws If either beat is not finite, or `toBeat` precedes `fromBeat`.\n */\n slice(fromBeat: number, toBeat: number): Timeline {\n assertFiniteNumber(fromBeat, 'timeline slice fromBeat');\n assertFiniteNumber(toBeat, 'timeline slice toBeat');\n if (toBeat < fromBeat) {\n throw new InvalidInputError(\n `timeline slice toBeat must not precede fromBeat; received [${fromBeat}, ${toBeat})`,\n );\n }\n const clip = <T extends { startBeat: number; endBeat: number }>(spans: readonly T[]): T[] =>\n spans\n .map((span) => clipSpan(span, fromBeat, toBeat))\n .filter((span): span is T => span !== null);\n // The confidences travel with the segments they describe: they are read by\n // position, so dropping a segment without dropping its reading would shift\n // every later one onto the wrong chord.\n const kept = this.#segments\n .map((segment, index) => ({ segment, index }))\n .filter(({ segment }) => clipSpan(segment, fromBeat, toBeat) !== null);\n const data: TimelineData = {\n segments: clip(this.#segments),\n totalBeats: Math.max(0, Math.min(toBeat, this.#totalBeats)),\n keys: clip(this.#keys),\n };\n if (this.#segmentConfidence.length > 0) {\n data.segmentConfidence = kept.map(({ index }) => this.#segmentConfidence[index] ?? 0);\n }\n return new Timeline(data);\n }\n\n /**\n * The timeline moved by a number of semitones.\n *\n * The key regions move with the chords, so every segment keeps the degree and\n * function it had in the key it sounded in.\n *\n * @param semitones The signed semitone offset.\n * @returns The transposed timeline.\n */\n transpose(semitones: number): Timeline {\n return this.#moved(\n (chord) => chord.transpose(semitones),\n (key) => key.transpose(semitones),\n );\n }\n\n /**\n * The timeline moved by a spelled interval, keeping the spelling.\n *\n * Unlike {@link Timeline.transpose}, which picks letters from a semitone\n * count, the interval's diatonic number decides them: a chord taken up an\n * augmented fourth is spelled with sharps and one taken up a diminished fifth\n * with flats.\n *\n * Only a chord carrying a spelling of its own is spelled that way. A key\n * region holds a key/scale rather than a spelled key, so a chord that takes\n * its letters from the key in force follows the spelling that scale reads\n * best from, whichever interval moved it there.\n *\n * @param interval An interval name (e.g. `'A4'`, `'-m3'`), plain interval\n * data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed timeline.\n */\n transposeBy(interval: IntervalLike): Timeline {\n return this.#moved(\n (chord) => chord.transposeBy(interval),\n (key) => key.transposeBy(interval),\n );\n }\n\n /**\n * Whether another timeline holds the same chords over the same span.\n *\n * The key regions are not compared: they are an analysis lens over the\n * chords, the way a {@link Progression}'s key context is over its own.\n *\n * @param other The timeline to compare.\n * @returns True when the segments and the span both match.\n */\n equals(other: Timeline): boolean {\n const theirs = other.data;\n if (theirs.totalBeats !== this.#totalBeats || theirs.segments.length !== this.length) {\n return false;\n }\n return this.#segments.every((segment, index) => {\n const mine = segment;\n const yours = theirs.segments[index];\n return (\n yours !== undefined &&\n mine.startBeat === yours.startBeat &&\n mine.endBeat === yours.endBeat &&\n new ChordClass(mine.chord).equals(new ChordClass(yours.chord))\n );\n });\n }\n\n /** A copy of the underlying plain data. */\n get data(): TimelineData {\n const data: TimelineData = {\n segments: this.#copySegments(),\n totalBeats: this.#totalBeats,\n };\n // Omitted rather than written as an empty list, so a timeline built without\n // a key serializes to the segments and the span alone.\n if (this.#keys.length > 0) {\n data.keys = this.#keys.map(copyKeyRegion);\n }\n if (this.#segmentConfidence.length > 0) {\n data.segmentConfidence = [...this.#segmentConfidence];\n }\n return data;\n }\n\n /** The plain form of the timeline, for `JSON.stringify`. */\n toJSON(): TimelineData {\n return this.data;\n }\n\n /** The `{ at, segments }` shape the analysis functions take. */\n get chordTimeline(): ChordTimeline {\n const segments = this.#copySegments();\n return { at: (beat) => chordAtBeat(segments, beat), segments };\n }\n\n /** A fresh copy of the segments, which is what every reader is handed. */\n #copySegments(): ChordSegment[] {\n return this.#segments.map(copySegment);\n }\n\n /** The key in force at a beat, or undefined when the timeline knows none. */\n #keyAt(beat: number): Key | undefined {\n const prevailing = prevailingKeyOf(this.#keys);\n return prevailing === null ? undefined : KeyClass.of(keyLookup(this.#keys, prevailing)(beat));\n }\n\n /**\n * The key every beat of the timeline is read against.\n *\n * A lookup rather than one key, so a timeline that modulates has each chord\n * analyzed in the key it sounds in; beats outside every region fall back to\n * the prevailing key.\n */\n #keyContext(): (beat: number) => KeyScale {\n const prevailing = prevailingKeyOf(this.#keys);\n if (prevailing === null) {\n throw new InvalidInputError(\n 'timeline carries no key; build it from notes, or pass a key to Timeline.fromChords',\n );\n }\n return keyLookup(this.#keys, prevailing);\n }\n\n /** Move every chord and every key region by the same step. */\n #moved(moveChord: (chord: Chord) => Chord, moveKey: (key: Key) => Key): Timeline {\n const segments = this.#segments.map((segment) => ({\n startBeat: segment.startBeat,\n endBeat: segment.endBeat,\n chord: moveChord(new ChordClass(segment.chord)).toJSON(),\n }));\n const keys = this.#keys.map((region) => {\n const moved: KeyRegion = { ...region, key: moveKey(KeyClass.of(region.key)).scale };\n if (region.pivot !== undefined) {\n // The numerals a pivot carries are degrees, so they survive the move;\n // only the chord they name has to travel with the keys.\n moved.pivot = {\n ...region.pivot,\n chord: moveChord(new ChordClass(region.pivot.chord)).toJSON(),\n };\n }\n return moved;\n });\n const data: TimelineData = { segments, totalBeats: this.#totalBeats, keys };\n // Transposition moves the chords without re-reading them, so each segment\n // is exactly as well attested where it lands as where it came from.\n if (this.#segmentConfidence.length > 0) {\n data.segmentConfidence = [...this.#segmentConfidence];\n }\n return new Timeline(data);\n }\n}\n","import {\n type AnalyzeChordOptions,\n type CadenceResult,\n type ChordAnalysis,\n type ChordToRomanOptions,\n type DetectCadenceOptions,\n detectCadence,\n type HarmonicFunction,\n} from '../analyze/functional/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { IntervalLike } from '../core/pitch/index.js';\nimport type { SubstituteOptions, SubstitutionType } from '../generate/reharmony/index.js';\nimport { substituteChord } from '../generate/reharmony/index.js';\nimport type { Chord as ChordData, ChordSpan } from '../theory/chord/index.js';\nimport { chordFromSpan } from '../theory/chord/index.js';\nimport { type ScaleChoice, scalesForChanges } from '../theory/chordscale/index.js';\nimport { type VoicingOptions, voiceProgression } from '../theory/voicing/index.js';\nimport type { Chord } from './chord.js';\nimport { Chord as ChordClass } from './chord.js';\nimport type { Key, KeyData } from './key.js';\nimport { Key as KeyClass } from './key.js';\nimport { assertKeyArgument } from './shared.js';\nimport { Timeline } from './timeline.js';\n\n/**\n * The plain form of a {@link Progression}: the chords as plain data, and the\n * carried key as its own plain data when the progression has one.\n */\nexport type ProgressionData = { chords: ChordData[]; key: KeyData | undefined };\n\n/**\n * An immutable ordered sequence of chords, optionally carrying a {@link Key}\n * context shared by its analysis methods.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * const key = Key.major('C');\n * key.chord(2).progressionTo(key.chord(5), key.chord(1)).roman();\n * // ['ii', 'V', 'I']\n * ```\n */\nexport class Progression {\n readonly #chords: readonly Chord[];\n readonly #key: Key | undefined;\n\n /**\n * Wrap a chord sequence.\n *\n * @param chords The chords in order; the array is copied.\n * @param key Optional key context for analysis methods.\n */\n constructor(chords: readonly Chord[], key?: Key) {\n this.#chords = Object.freeze(Progression.#attachKey(chords, key));\n this.#key = key;\n }\n\n /**\n * Attach the progression key to every chord member.\n *\n * The key is applied unconditionally, including to chords that already carry\n * one, so the members never disagree with the progression about which key\n * spells them and a re-key during a modulation reaches all of them.\n * {@link Chord.withKey} keeps a spelling the caller supplied and re-derives\n * the rest, so the result depends on neither the chords' previous key nor the\n * number of times a key was attached.\n */\n static #attachKey(chords: readonly Chord[], key: Key | undefined): Chord[] {\n return key === undefined ? [...chords] : chords.map((chord) => chord.withKey(key));\n }\n\n /**\n * Start from a chord sequence, matching the `of` factory on the other\n * classes.\n *\n * The same reading as the constructor, so a progression reads like\n * `Chord.of` and `Score.of` do at the point it is built.\n *\n * @param chords The chords in order; the array is copied.\n * @param key Optional key context for the analysis methods.\n * @returns The progression.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * Progression.of([Chord.parse('C'), Chord.parse('G7')], Key.major('C')).roman();\n * // ['I', 'V7']\n * ```\n */\n static of(chords: readonly Chord[], key?: Key): Progression {\n return new Progression(chords, key);\n }\n\n /**\n * Build a progression from the {@link ChordSpan} records the generators\n * return, keeping their order.\n *\n * The spans' `startBeat`, `degree`, and `secondaryDominant` are analysis\n * annotations that a chord sequence does not carry; only the harmony crosses\n * over. Keep the spans themselves if the timing matters.\n *\n * @param spans The chord spans, in order.\n * @param key Optional key context for the analysis methods.\n * @returns The progression.\n * @example\n * ```ts\n * import { generateProgression, Key, Progression } from '@libraz/libcantus';\n * const key = Key.major('C');\n * const spans = generateProgression({ key: key.scale, style: 'dance', bars: 4 });\n * Progression.fromSpans(spans, key).roman();\n * ```\n */\n static fromSpans(spans: readonly ChordSpan[], key?: Key): Progression {\n const chords = spans.map((span) => ChordClass.fromData(chordFromSpan(span)));\n return new Progression(chords, key);\n }\n\n /**\n * Rebuild a progression from its {@link Progression.toJSON} output.\n *\n * @param data The serialized chords and key.\n * @returns The progression.\n */\n static fromJSON(data: ProgressionData): Progression {\n const key = data.key === undefined ? undefined : KeyClass.fromJSON(data.key);\n const chords = data.chords.map((chord) => ChordClass.fromJSON(chord));\n return key === undefined ? new Progression(chords) : new Progression(chords, key);\n }\n\n /**\n * Wrap plain progression data, matching the `fromData` factory on the other\n * classes.\n *\n * @param data The plain progression, as {@link Progression.data} hands it out.\n * @returns The progression.\n */\n static fromData(data: ProgressionData): Progression {\n return Progression.fromJSON(data);\n }\n\n /**\n * The chord sequence.\n *\n * The array is the progression's own and is frozen rather than copied, so\n * reading it in a loop stays linear.\n */\n get chords(): readonly Chord[] {\n return this.#chords;\n }\n\n /** The carried key context, if any. */\n get key(): Key | undefined {\n return this.#key;\n }\n\n /**\n * A copy of the underlying plain progression data: the chords as plain\n * objects and the carried key, if any, as its own plain data.\n */\n get data(): ProgressionData {\n return this.toJSON();\n }\n\n /** The number of chords. */\n get length(): number {\n return this.#chords.length;\n }\n\n /**\n * The chord at an index.\n *\n * @param index 0-based position; a negative index counts from the end.\n * @returns The chord, or undefined when the index is out of range.\n */\n at(index: number): Chord | undefined {\n return this.#chords.at(index);\n }\n\n /** Iterate the chords in order, so a progression works with `for...of`. */\n [Symbol.iterator](): IterableIterator<Chord> {\n return this.#chords[Symbol.iterator]();\n }\n\n /**\n * Whether another progression holds the same chords in the same order.\n *\n * The key context is not compared: it is an analysis lens, not part of the\n * harmony. Chord equality follows {@link Chord.equals}.\n *\n * @param other The progression to compare.\n * @returns True when the chord sequences match.\n */\n equals(other: Progression): boolean {\n if (this.#chords.length !== other.length) {\n return false;\n }\n return this.#chords.every((chord, index) => {\n const theirs = other.at(index);\n return theirs !== undefined && chord.equals(theirs);\n });\n }\n\n /**\n * A copy of this progression with a chord appended.\n *\n * @param chord The chord to append.\n * @returns The new progression.\n */\n add(chord: Chord): Progression {\n return new Progression([...this.#chords, chord], this.#key);\n }\n\n /**\n * A new progression with every chord replaced by what `fn` returns for it.\n *\n * The result is a progression rather than an array, and it carries this\n * progression's key context, so a transformation stays inside the class\n * instead of dropping out of it and having to be rebuilt. This progression is\n * untouched.\n *\n * The key reaches the returned chords the way the constructor attaches it, so\n * a chord `fn` built without one is still spelled and analyzed in this key.\n *\n * @param fn Called with each chord and its 0-based index.\n * @returns The mapped progression.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('C'), Chord.parse('G')], Key.major('C'));\n * const moved = progression.map((chord) => chord.transpose(2));\n * moved.toString(); // 'D A'\n * moved.key?.toString(); // 'C major'\n * progression.toString(); // 'C G' — the original is unchanged\n * ```\n */\n map(fn: (chord: Chord, index: number) => Chord): Progression {\n return new Progression(\n this.#chords.map((chord, index) => fn(chord, index)),\n this.#key,\n );\n }\n\n /**\n * A new progression holding only the chords `pred` accepts, in order.\n *\n * The key context travels with them and this progression is untouched.\n *\n * @param pred Called with each chord and its 0-based index.\n * @returns The filtered progression, empty when nothing is accepted.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression(\n * [Chord.parse('C'), Chord.parse('Am'), Chord.parse('F'), Chord.parse('G')],\n * Key.major('C'),\n * );\n * progression.filter((chord) => chord.quality === 'maj').toString(); // 'C F G'\n * ```\n */\n filter(pred: (chord: Chord, index: number) => boolean): Progression {\n return new Progression(\n this.#chords.filter((chord, index) => pred(chord, index)),\n this.#key,\n );\n }\n\n /**\n * A stretch of this progression, as a progression carrying the same key.\n *\n * The bounds read as `Array.prototype.slice` reads them: `end` is exclusive,\n * a negative index counts from the end, and an omitted bound runs to the edge.\n *\n * @param start First chord of the stretch; defaults to the beginning.\n * @param end One past the last chord; defaults to the end.\n * @returns The sliced progression.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression(\n * [Chord.parse('C'), Chord.parse('Am'), Chord.parse('F')],\n * Key.major('C'),\n * );\n * progression.slice(1).toString(); // 'Am F'\n * progression.slice(0, 2).toString(); // 'C Am'\n * ```\n */\n slice(start?: number, end?: number): Progression {\n return new Progression(this.#chords.slice(start, end), this.#key);\n }\n\n /**\n * This progression followed by another run of chords.\n *\n * The result carries this progression's key context; a key the other\n * progression carried is dropped, because two keys cannot both analyze one\n * chord sequence. Re-key the result with {@link Progression.withKey} where the\n * second run is the one to read it in.\n *\n * @param other The chords to append, as a progression or a plain array.\n * @returns The joined progression.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * const key = Key.major('C');\n * key.progression('I', 'vi').concat(key.progression('IV', 'V')).roman();\n * // ['I', 'vi', 'IV', 'V']\n * ```\n */\n concat(other: Progression | readonly Chord[]): Progression {\n // Read structurally rather than with `instanceof`: the package ships an ESM\n // and a CommonJS build, so a consumer reaching the two through different\n // conditions holds two `Progression` classes.\n const chords: readonly Chord[] = 'chords' in other ? other.chords : other;\n return new Progression([...this.#chords, ...chords], this.#key);\n }\n\n /**\n * The position of the first chord equal to `chord`.\n *\n * Equality is {@link Chord.equals}, not reference identity, so a chord built\n * separately is found as long as it names the same harmony.\n *\n * @param chord The chord to look for.\n * @returns The 0-based position, or -1 when the progression holds no such\n * chord.\n * @example\n * ```ts\n * import { Chord, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('C'), Chord.parse('G7')]);\n * progression.indexOf(Chord.of('G', 'dom7')); // 1\n * progression.indexOf(Chord.parse('F')); // -1\n * ```\n */\n indexOf(chord: Chord): number {\n return this.#chords.findIndex((mine) => mine.equals(chord));\n }\n\n /**\n * A copy of this progression carrying the given key context.\n *\n * @param key The key context to attach.\n * @returns The new progression.\n */\n withKey(key: Key): Progression {\n return new Progression(this.#chords, key);\n }\n\n /**\n * Voice the progression with smooth voice leading.\n *\n * @param opts Voicing options; defaults to four SATB voices.\n * @returns One ascending voicing (MIDI pitches) per chord.\n * @throws If any chord admits no voicing within the given ranges.\n */\n voice(opts?: VoicingOptions): number[][] {\n const key = opts?.key ?? this.#key?.scale;\n return voiceProgression(\n this.#chords.map((chord) => chord.data),\n key === undefined ? opts : { ...opts, key },\n );\n }\n\n /**\n * The Roman numeral of each chord in a key.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @param opts Applied-numeral rendering options.\n * @returns One numeral per chord.\n * @throws If no key is given and none is carried.\n */\n roman(key?: Key, opts?: ChordToRomanOptions): string[] {\n const resolved = this.#resolveKey(key);\n return this.#chords.map((chord) => chord.roman(resolved, opts));\n }\n\n /**\n * The harmonic function of each chord in a key.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @returns One function per chord.\n * @throws If no key is given and none is carried.\n */\n functions(key?: Key): HarmonicFunction[] {\n const resolved = this.#resolveKey(key);\n return this.#chords.map((chord) => chord.function(resolved));\n }\n\n /**\n * Analyze every chord and classify the closing cadence.\n *\n * The cadence is detected on the final chord pair and is null when the\n * progression has fewer than two chords.\n *\n * The options reach both analyses: `applied` and `alternatives` go to every\n * chord, and `voicing` — the pitches of the last two chords, as\n * {@link Progression.voice} produces them — to the cadence, which cannot tell\n * a perfect authentic cadence from an imperfect one without knowing the\n * soprano.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @param opts Applied-numeral rendering options, `alternatives` for the\n * readings both analyses turned down, and `voicing` for the cadence's\n * voice-leading detail; see {@link AnalyzeChordOptions} and\n * {@link DetectCadenceOptions}.\n * @returns Per-chord analyses and the closing cadence.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('G7'), Chord.parse('C')], Key.major('C'));\n * const voiced = progression.voice();\n * progression.analyze(undefined, { voicing: [voiced[0] ?? [], voiced[1] ?? []] }).cadence\n * ?.strength; // 'perfect'\n * ```\n */\n analyze(\n key?: Key,\n opts?: AnalyzeChordOptions & DetectCadenceOptions,\n ): { chords: ChordAnalysis[]; cadence: CadenceResult | null } {\n const resolved = this.#resolveKey(key);\n const chords = this.#chords.map((chord) => chord.analyze(resolved, opts));\n const from = this.#chords[this.#chords.length - 2];\n const to = this.#chords[this.#chords.length - 1];\n const cadence =\n from !== undefined && to !== undefined\n ? detectCadence(from.data, to.data, resolved.scale, opts)\n : null;\n return { chords, cadence };\n }\n\n /**\n * Choose one compatible scale for every chord, favoring smooth changes.\n *\n * @returns One scale choice per chord in this progression.\n */\n scales(): ScaleChoice[] {\n return scalesForChanges(this.#chords.map((chord) => chord.data));\n }\n\n /**\n * Classify the motion at every chord change, not only the closing one.\n *\n * One entry per adjacent pair, so entry `i` is the motion from chord `i` to\n * chord `i + 1` and the list is one shorter than the progression. A pair that\n * cadences not at all keeps its place with a null `type`, which is what lets a\n * caller read a cadence back against the chord it arrives on — a progression\n * has no beats to name it by.\n *\n * The chord before each pair is supplied from the progression itself, so a\n * cadential six-four is recognized as one wherever it stands.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @param opts `voicing` is the pitches sounding under the whole progression,\n * one voicing per chord as {@link Progression.voice} produces them, from\n * which each pair takes its own two — without it no authentic cadence can be\n * graded perfect or imperfect. `alternatives` collects the readings each\n * pair came close to, and `approach` is the chord sounding before the\n * progression began, which only the first pair has no predecessor of its\n * own for. See {@link DetectCadenceOptions}, whose fields these are.\n * @returns One cadence per adjacent pair, in order.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression(\n * [Chord.parse('C'), Chord.parse('F'), Chord.parse('G7'), Chord.parse('C')],\n * Key.major('C'),\n * );\n * progression.cadences().map((cadence) => cadence.type); // [null, 'half', 'authentic']\n * ```\n */\n cadences(\n key?: Key,\n opts?: { voicing?: number[][]; alternatives?: boolean; approach?: Chord },\n ): CadenceResult[] {\n const resolved = this.#resolveKey(key);\n const found: CadenceResult[] = [];\n for (let index = 1; index < this.#chords.length; index += 1) {\n const from = this.#chords[index - 1];\n const to = this.#chords[index];\n if (from === undefined || to === undefined) {\n continue;\n }\n const approach = index >= 2 ? this.#chords[index - 2] : opts?.approach;\n const pair = opts?.voicing;\n found.push(\n detectCadence(from.data, to.data, resolved.scale, {\n ...(opts?.alternatives === undefined ? {} : { alternatives: opts.alternatives }),\n ...(approach === undefined ? {} : { approach: approach.data }),\n ...(pair === undefined\n ? {}\n : { voicing: [pair[index - 1] ?? [], pair[index] ?? []] as [number[], number[]] }),\n }),\n );\n }\n return found;\n }\n\n /**\n * A copy of this progression with one chord replaced by a substitute.\n *\n * The substitutions are the ones {@link substituteChord} proposes for that\n * chord in this progression's key, and the first of the named kind is taken.\n * Each is spelled the way the key writes it, so the tritone substitute of G7\n * in C major arrives as Db7 rather than C#7.\n *\n * @param index 0-based position of the chord to replace; a negative index\n * counts from the end, as {@link Progression.at} counts it.\n * @param type Which substitution relationship to realize.\n * @param opts `melodyPcs` keeps only substitutes that contain those pitch\n * classes, so a melody stays consonant against the new harmony; see\n * {@link SubstituteOptions}.\n * @returns The progression with the substitute in place.\n * @throws If the index names no chord, if the progression carries no key\n * context, or if that chord has no substitution of the named kind.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('G7'), Chord.parse('C')], Key.major('C'));\n * progression.substitute(0, 'tritone').toString(); // 'Db7 C'\n * ```\n */\n substitute(index: number, type: SubstitutionType, opts?: SubstituteOptions): Progression {\n const position = index < 0 ? this.#chords.length + index : index;\n const target = this.#chords[position];\n if (target === undefined) {\n throw new InvalidInputError(\n `progression has no chord at index ${index}; it holds ${this.#chords.length}`,\n );\n }\n const key = this.#resolveKey();\n const chosen = substituteChord(target.data, key.scale, opts).find(\n (candidate) => candidate.type === type,\n );\n if (chosen === undefined) {\n throw new InvalidInputError(\n `no ${type} substitution for ${target.symbol()} in ${key.toString()}`,\n );\n }\n const chords = [...this.#chords];\n chords[position] = new ChordClass(chosen.chord);\n return new Progression(chords, key);\n }\n\n /**\n * Place the chords on a regular grid, giving each the same number of beats.\n *\n * The inverse of {@link Timeline.progression}, which drops the time axis\n * again. A carried key becomes the one key region under the span.\n *\n * @param beatsEach How long each chord sounds.\n * @returns The timeline.\n * @throws If `beatsEach` is not a positive finite number of beats.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('C'), Chord.parse('G7')], Key.major('C'));\n * progression.timeline(4).at(5)?.symbol(); // 'G7'\n * ```\n */\n timeline(beatsEach: number): Timeline {\n return Timeline.fromProgression(this, beatsEach);\n }\n\n /**\n * Transpose every chord by a number of semitones.\n *\n * A carried key moves with the chords, so the progression keeps its degrees\n * and functions in the new key.\n *\n * @param semitones The signed semitone offset.\n * @returns The transposed progression.\n * @example\n * ```ts\n * import { Chord, Progression } from '@libraz/libcantus';\n * new Progression([Chord.parse('C'), Chord.parse('G')]).transpose(2).toString(); // 'D A'\n * ```\n */\n transpose(semitones: number): Progression {\n const key = this.#key?.transpose(semitones);\n // The constructor hands the transposed key to every member, so a chord\n // spelled by this progression's key follows it instead of falling back to\n // sharps in a flat key.\n const chords = this.#chords.map((chord) => chord.transpose(semitones));\n return new Progression(chords, key);\n }\n\n /**\n * Transpose every chord by a spelled interval.\n *\n * Unlike {@link Progression.transpose}, which picks letters from a semitone\n * count, the interval's diatonic number decides them, so a progression taken\n * up an augmented fourth is spelled with sharps and one taken up a diminished\n * fifth with flats. A carried key moves with the chords.\n *\n * @param interval An interval name (e.g. `'A4'`, `'-m3'`), plain interval\n * data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed progression.\n * @example\n * ```ts\n * import { Chord, Progression } from '@libraz/libcantus';\n * new Progression([Chord.parse('C'), Chord.parse('G7')]).transposeBy('A4').toString();\n * // 'F# C#7'\n * ```\n */\n transposeBy(interval: IntervalLike): Progression {\n const key = this.#key?.transposeBy(interval);\n // The constructor hands the transposed key to every member, so a chord\n // spelled by this progression's key follows it instead of falling back to\n // sharps in a flat key.\n const chords = this.#chords.map((chord) => chord.transposeBy(interval));\n return new Progression(chords, key);\n }\n\n /**\n * Transpose the progression so that its key becomes `target`.\n *\n * The interval between the two tonics moves the chords and decides their\n * spelling, so moving C major to Gb major writes flats while moving it to F#\n * major writes sharps. The result carries `target` itself, mode included: the\n * chords move by interval but the key is replaced rather than transposed, so\n * a target in another mode — a relative, parallel, or modal key — is the key\n * the progression is then analyzed in.\n *\n * @param target The key the transposed progression should be in.\n * @returns The transposed progression, carrying `target` as its key.\n * @throws If the progression carries no key context to measure from.\n * @example\n * ```ts\n * import { Chord, Key, Progression } from '@libraz/libcantus';\n * const progression = new Progression([Chord.parse('C'), Chord.parse('G7')], Key.major('C'));\n * progression.transposeTo(Key.major('Eb')).toString(); // 'Eb Bb7'\n * ```\n */\n transposeTo(target: Key): Progression {\n const from = this.#key;\n if (from === undefined) {\n throw new InvalidInputError(\n 'progression has no key context; attach one with withKey() before transposing to another key',\n );\n }\n const interval = from.intervalTo(target);\n const chords = this.#chords.map((chord) => chord.transposeBy(interval));\n return new Progression(chords, target);\n }\n\n /**\n * The chord symbols separated by spaces, so a template literal or a log line\n * reads as the progression.\n *\n * @returns The symbols in order, e.g. `'C Am F G'`.\n */\n toString(): string {\n return this.#chords.map((chord) => chord.symbol()).join(' ');\n }\n\n /**\n * The plain progression data, for JSON serialization.\n *\n * Private class fields do not serialize, so this preserves both the chord\n * data and any carried key in `JSON.stringify(progression)`.\n *\n * @returns The chord data sequence and the carried key, if any.\n */\n toJSON(): ProgressionData {\n return {\n chords: this.#chords.map((chord) => chord.toJSON()),\n key: this.#key?.toJSON(),\n };\n }\n\n /** Resolve the key for an analysis method: explicit first, then carried. */\n #resolveKey(key?: Key): Key {\n assertKeyArgument(key, 'progression key');\n const resolved = key ?? this.#key;\n if (resolved === undefined) {\n throw new InvalidInputError(\n 'progression has no key context; pass a Key or attach one with withKey()',\n );\n }\n return resolved;\n }\n}\n","import type { ChordMatch, DetectChordOptions } from '../analyze/detect/index.js';\nimport { detectChord, detectChordBest } from '../analyze/detect/index.js';\nimport {\n type AnalyzeChordOptions,\n analyzeChord,\n type BorrowedSource,\n borrowedSource,\n type ChordAnalysis,\n type ChordToRomanOptions,\n chordToRoman,\n type ExplainRomanOptions,\n explainRoman,\n functionOf,\n type HarmonicFunction,\n isBorrowedChord,\n type RomanExplanation,\n secondaryDominantOf,\n} from '../analyze/functional/index.js';\nimport { InvalidInputError, type ParseResult, unwrapParse } from '../core/errors/index.js';\nimport type {\n IntervalLike,\n Note as NoteData,\n NoteLike,\n NoteNameOptions,\n SpelledInterval,\n} from '../core/pitch/index.js';\nimport {\n noteToPitchClass,\n pitchClassOf,\n toNoteData,\n toSpelledInterval,\n transposeByInterval,\n transposeNote,\n} from '../core/pitch/index.js';\nimport type { KeyScale } from '../core/types.js';\nimport { assertFiniteNumber } from '../core/validation/index.js';\nimport {\n type BorrowedChord,\n modalInterchangePalette,\n negativeHarmonyMirror,\n type SubstituteOptions,\n type Substitution,\n substituteChord,\n} from '../generate/reharmony/index.js';\nimport {\n type Chord as ChordData,\n type ChordQuality,\n type ChordSpan,\n type ChordSpec,\n type ChordToneRole,\n chordPitchClasses,\n chordSpecOf,\n chordToneRole,\n chordToneSpellings,\n isChordMember,\n makeChord,\n type PitchSpelling,\n spanFromChord,\n transposeChord,\n} from '../theory/chord/index.js';\nimport {\n type AvailableTensionsOptions,\n type AvoidNotesOptions,\n availableTensions,\n avoidNotes,\n type ChordScaleMatch,\n type ChordScaleReportEntry,\n chordScaleReport,\n chordScales,\n} from '../theory/chordscale/index.js';\nimport { figuredBassOf, realizeFiguredBass } from '../theory/figured-bass/index.js';\nimport { type KeyLike, type ScaleNameInput, toKeyScale } from '../theory/scale/index.js';\nimport { spellChord, spellChordFromRoot, spellPitchClass } from '../theory/spelling/index.js';\nimport {\n type ChordSymbolOptions,\n formatChordSymbol,\n tryParseChordSymbol,\n} from '../theory/symbol/index.js';\nimport {\n type StyledVoicingOptions,\n type VoicingOptions,\n voiceChord,\n voiceChordStyled,\n} from '../theory/voicing/index.js';\nimport type { Key } from './key.js';\nimport { Note } from './note.js';\nimport { Progression } from './progression.js';\nimport { assertKeyArgument, mod12 } from './shared.js';\n\n/** Whether a spelling hint still names the pitch class it is attached to. */\nfunction hintMatches(hint: PitchSpelling | undefined, pc: number | undefined): boolean {\n return hint !== undefined && pc !== undefined && mod12(noteToPitchClass(hint)) === mod12(pc);\n}\n\n/** Move an explicit spelling hint without turning it into a derived key spelling. */\nfunction transposeHint(\n hint: PitchSpelling | undefined,\n semitones: number,\n): PitchSpelling | undefined {\n if (hint === undefined) {\n return undefined;\n }\n const moved = transposeNote(hint, semitones);\n return { letter: moved.letter, alter: moved.alter };\n}\n\n/**\n * Move an explicit spelling hint by a spelled interval.\n *\n * The interval's diatonic number picks the letter, so a C chord taken up an\n * augmented fourth is spelled F# and up a diminished fifth Gb — the distinction\n * {@link transposeHint} cannot make from a semitone count alone.\n */\nfunction transposeHintByInterval(\n hint: PitchSpelling | undefined,\n interval: SpelledInterval,\n): PitchSpelling | undefined {\n if (hint === undefined) {\n return undefined;\n }\n const moved = transposeByInterval(hint, interval);\n return { letter: moved.letter, alter: moved.alter };\n}\n\n/**\n * Move a chord's per-tone spelling hints with the chord.\n *\n * Every tone takes the same letter distance, so the distances between them\n * survive: the German sixth's augmented sixth is still five letters above its\n * root afterwards, whichever letters the two of them land on.\n */\nfunction transposeToneHints(\n hints: PitchSpelling[] | undefined,\n semitones: number,\n): PitchSpelling[] | undefined {\n return hints?.map((hint) => {\n const moved = transposeNote(hint, semitones);\n return { letter: moved.letter, alter: moved.alter };\n });\n}\n\n/** Move a chord's per-tone spelling hints by a spelled interval. */\nfunction transposeToneHintsByInterval(\n hints: PitchSpelling[] | undefined,\n interval: SpelledInterval,\n): PitchSpelling[] | undefined {\n return hints?.map((hint) => {\n const moved = transposeByInterval(hint, interval);\n return { letter: moved.letter, alter: moved.alter };\n });\n}\n\n/**\n * Defensive copy of a plain chord.\n *\n * The root and bass are reduced to pitch classes by the same helper\n * {@link makeChord} uses, because this is the other way a chord is built: data\n * arriving from a project file or a plugin would otherwise keep a root of 25\n * that every getter reports verbatim and `equals` compares against a 1 it never\n * matches.\n *\n * Enharmonic spelling hints (`rootSpelling`/`bassSpelling`, populated by\n * `parseChordSymbol`, and the per-tone `toneSpellings` an augmented sixth\n * carries) are carried through so a flat-named chord round-trips through the\n * class API — but only while they still name their own pitch class. A stale\n * hint is dropped here rather than passed on, so that everything downstream,\n * including the derived bass spelling, reads a hint it can trust.\n */\nfunction copyChord(data: ChordData): ChordData {\n const copy: ChordData = {\n rootPc: pitchClassOf(data.rootPc),\n quality: data.quality,\n // Each offset is checked rather than copied blind: a chord holding a NaN\n // interval voices, spells and formats as a chord that looks real, and the\n // failure surfaces wherever the number is finally used.\n intervals: data.intervals.map((interval, index) =>\n assertFiniteNumber(interval, `chord intervals[${index}]`),\n ),\n };\n if (data.bassPc !== undefined) {\n copy.bassPc = pitchClassOf(data.bassPc);\n }\n const tones = chordToneSpellings(data);\n if (tones !== undefined) {\n copy.toneSpellings = tones;\n }\n if (hintMatches(data.rootSpelling, data.rootPc) && data.rootSpelling !== undefined) {\n copy.rootSpelling = { letter: data.rootSpelling.letter, alter: data.rootSpelling.alter };\n }\n if (hintMatches(data.bassSpelling, data.bassPc) && data.bassSpelling !== undefined) {\n copy.bassSpelling = { letter: data.bassSpelling.letter, alter: data.bassSpelling.alter };\n }\n return copy;\n}\n\n/**\n * Spell the slash bass of a chord whose root spelling is already settled.\n *\n * A bass that is one of the chord's own tones takes that tone's spelling, so\n * `Eb/Bb` never renders as `Eb/A#`; a bass outside the chord is spelled by the\n * key when one is available.\n */\nfunction deriveBassSpelling(chord: ChordData, key: Key | undefined): PitchSpelling | undefined {\n const bassPc = chord.bassPc;\n if (bassPc === undefined) {\n return undefined;\n }\n const root = chord.rootSpelling;\n if (root !== undefined) {\n const index = chord.intervals.findIndex((i) => mod12(chord.rootPc + i) === bassPc);\n const tone = index >= 0 ? spellChordFromRoot(chord, root)[index] : undefined;\n if (tone !== undefined) {\n return { letter: tone.letter, alter: tone.alter };\n }\n }\n if (key === undefined) {\n return undefined;\n }\n const spelled = spellPitchClass(bassPc, key.tonic.data, key.scale);\n return { letter: spelled.letter, alter: spelled.alter };\n}\n\n/**\n * An immutable chord: a root pitch class, quality, interval template, and\n * optional slash bass, optionally carrying a {@link Key} context. Analysis\n * methods (`roman`, `function`, `analyze`, ...) use an explicitly passed key\n * first and fall back to the carried context.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Cmaj7').invert(1).symbol(); // 'Cmaj7/E' (third in the bass)\n * ```\n */\nexport class Chord {\n /** Exactly what the caller supplied: any spelling here is the caller's own. */\n readonly #given: ChordData;\n readonly #key: Key | undefined;\n\n /**\n * Wrap a plain chord object.\n *\n * @param data The chord; it is copied, never retained or mutated.\n * @param key Optional key context for analysis methods.\n */\n constructor(data: ChordData, key?: Key) {\n this.#given = copyChord(data);\n this.#key = key;\n }\n\n /**\n * The chord data with any missing spelling hint filled in from the key.\n *\n * Derived spellings are computed on read rather than baked in at construction,\n * so re-attaching a different key re-spells the chord instead of carrying the\n * first key's letters forever. A spelling the caller supplied always wins.\n */\n get #data(): ChordData {\n return this.#dataWithKey(this.#key);\n }\n\n /** Build a read view whose missing spellings are derived from `key`. */\n #dataWithKey(key: Key | undefined): ChordData {\n const out = copyChord(this.#given);\n if (out.rootSpelling === undefined && key !== undefined) {\n out.rootSpelling = spellPitchClass(out.rootPc, key.tonic.data, key.scale);\n }\n if (out.bassSpelling === undefined) {\n const bass = deriveBassSpelling(out, key);\n if (bass !== undefined) {\n out.bassSpelling = bass;\n }\n }\n return out;\n }\n\n /**\n * Build a chord from a root and quality.\n *\n * @param root Root as a note name (e.g. `'Eb'`) or a pitch class.\n * @param quality The chord quality.\n * @param bass Optional slash-chord bass, as a note name (e.g. `'Bb'`) or a\n * pitch class. A named bass keeps its own spelling.\n * @returns The chord (without key context).\n */\n static of(root: string | number, quality: ChordQuality, bass?: string | number): Chord {\n const bassNote = typeof bass === 'string' ? Note.parse(bass) : undefined;\n const bassPc = bassNote !== undefined ? bassNote.pitchClass : (bass as number | undefined);\n const rootNote = typeof root === 'string' ? Note.parse(root) : undefined;\n const data = makeChord(\n rootNote !== undefined ? rootNote.pitchClass : (root as number),\n quality,\n bassPc,\n );\n if (rootNote !== undefined) {\n data.rootSpelling = { letter: rootNote.letter, alter: rootNote.alter };\n }\n if (bassNote !== undefined) {\n data.bassSpelling = { letter: bassNote.letter, alter: bassNote.alter };\n }\n return new Chord(data);\n }\n\n /**\n * Wrap an existing plain chord object.\n *\n * @param data The plain chord.\n * @returns The wrapped chord (without key context).\n */\n static fromData(data: ChordData): Chord {\n return new Chord(data);\n }\n\n /**\n * Rebuild a chord from its {@link Chord.toJSON} output.\n *\n * The key context is not serialized, so the result carries none; re-attach\n * one with {@link Chord.withKey}.\n *\n * @param data The serialized chord.\n * @returns The wrapped chord.\n */\n static fromJSON(data: ChordData): Chord {\n return new Chord(data);\n }\n\n /**\n * The chord a figured bass names: a bass note, the figures written under it,\n * and the key that supplies every interval the figures leave unaltered.\n *\n * The inverse of {@link Chord.figuredBass}, and a static because the figures\n * build a chord rather than read one. The result carries the given bass as\n * its slash bass, so an inversion reads as one wherever a bass is measured\n * against a root, and it keeps the letters the figures asked for. A moving\n * figure such as `4-3` yields the chord it resolves into.\n *\n * No key context is attached — the key here decides what the figures mean,\n * not how the chord is later analyzed — so use {@link Chord.withKey} to carry\n * one on.\n *\n * @param bass The bass note, spelled, as a note name, a MIDI number, or a\n * {@link Note}; its octave, if any, is not used.\n * @param figures The figures written under the bass; empty for an unfigured\n * note.\n * @param key The prevailing key; a key name, a plain key/scale, or a\n * {@link Key}.\n * @returns The chord the figures name (without key context).\n * @throws If the figures are malformed or name no chord, if the key is not\n * heptatonic, or if the figures sound a chord no {@link ChordQuality}\n * names.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.fromFiguredBass('B', '6', 'C major').symbol(); // 'G/B'\n * Chord.fromFiguredBass('G', '#', 'C minor').symbol(); // 'G'\n * ```\n */\n static fromFiguredBass(bass: NoteLike, figures: string, key: KeyLike): Chord {\n return new Chord(realizeFiguredBass(toNoteData(bass), figures, toKeyScale(key)));\n }\n\n /**\n * Parse a lead-sheet chord symbol (e.g. `'Cmaj7'`, `'F#m7b5'`, `'C/G'`).\n *\n * A symbol is English unless a system is asked for: unlike {@link Key.parse},\n * which reads the notation system off the name, `'B'` here is always the B\n * natural until `'german'` says otherwise.\n *\n * @param symbol The chord symbol.\n * @param opts `system` reads the root and bass in that notation system\n * instead of English.\n * @returns The chord (without key context).\n * @throws If the root or quality is not recognized. Use\n * {@link Chord.tryParse} where failure is ordinary, such as a chord field\n * read on every keystroke.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('H7', { system: 'german' }).symbol(); // 'B7'\n * ```\n */\n static parse(symbol: string, opts?: NoteNameOptions): Chord {\n return unwrapParse(Chord.tryParse(symbol, opts));\n }\n\n /**\n * Parse a lead-sheet chord symbol, reporting failure instead of throwing it.\n *\n * The same reading as {@link Chord.parse}, for the callers where a symbol\n * that does not parse yet is the normal state of the input rather than a\n * fault: a chord field can show what is wrong with what has been typed so far\n * without a `try` around every keystroke.\n *\n * @param symbol The chord symbol.\n * @param opts `system` reads the root and bass in that notation system\n * instead of English.\n * @returns The chord (without key context), or the error explaining why the\n * text is not one.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * const result = Chord.tryParse('Cmaj7(#11)');\n * result.ok ? result.value.symbol() : result.error.message; // 'Cmaj7#11'\n * ```\n */\n static tryParse(symbol: string, opts?: NoteNameOptions): ParseResult<Chord> {\n const parsed = tryParseChordSymbol(symbol, opts);\n return parsed.ok ? { ok: true, value: new Chord(parsed.value) } : parsed;\n }\n\n /**\n * Identify the chords matching a set of pitches, best interpretation first.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to interpret the input; see {@link DetectChordOptions}.\n * @returns Ranked chord interpretations (may be empty).\n */\n static detect(pitches: readonly number[], opts?: DetectChordOptions): Chord[] {\n return detectChord(pitches, opts).map(\n (match) => new Chord(makeChord(match.rootPc, match.quality, match.bassPc)),\n );\n }\n\n /**\n * Identify the chords matching a set of pitches, keeping each match's\n * recognition metadata beside the chord.\n *\n * {@link Chord.detect} discards the confidence signals a recognition UI needs\n * — whether the set matched exactly, which chord tones were missing, which\n * input notes were foreign, and which inversion the bass implies.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to interpret the input; see {@link DetectChordOptions}.\n * @returns Ranked interpretations, each with its chord and its match record.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * const [best] = Chord.detectMatches([60, 64, 67]);\n * best?.match.exact; // true\n * ```\n */\n static detectMatches(\n pitches: readonly number[],\n opts?: DetectChordOptions,\n ): { chord: Chord; match: ChordMatch }[] {\n return detectChord(pitches, opts).map((match) => ({\n chord: new Chord(makeChord(match.rootPc, match.quality, match.bassPc)),\n match,\n }));\n }\n\n /**\n * The single best chord interpretation of a pitch set.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to interpret the input; see {@link DetectChordOptions}.\n * @returns The top-ranked chord, or null when nothing matches.\n */\n static detectBest(pitches: readonly number[], opts?: DetectChordOptions): Chord | null {\n const best = detectChordBest(pitches, opts);\n return best === null ? null : new Chord(best);\n }\n\n /** The root pitch class (0..11). */\n get rootPc(): number {\n return this.#data.rootPc;\n }\n\n /** The chord quality. */\n get quality(): ChordQuality {\n return this.#data.quality;\n }\n\n /** A copy of the semitone offsets above the root. */\n get intervals(): number[] {\n return [...this.#data.intervals];\n }\n\n /** The slash-chord bass pitch class, or undefined in root position. */\n get bassPc(): number | undefined {\n return this.#data.bassPc;\n }\n\n /**\n * A copy of the underlying plain chord object, including the spellings\n * derived from any attached key.\n */\n get data(): ChordData {\n return this.#data;\n }\n\n /**\n * The chord read structurally: the triad it is built on, its seventh, and the\n * alterations, additions and omissions on top of them.\n *\n * The reading comes from the tones themselves, so a chord no symbol names —\n * a detected pitch set, a custom interval template — still describes itself;\n * only a template with no structural reading at all falls back to the one its\n * quality names.\n *\n * @throws If the chord's template has no reading and its quality is unknown.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Cmaj7').spec.base; // 'maj'\n * Chord.parse('C/E').spec.bassPc; // 4\n * ```\n */\n get spec(): ChordSpec {\n return chordSpecOf(this.#data);\n }\n\n /** The carried key context, if any. */\n get key(): Key | undefined {\n return this.#key;\n }\n\n /**\n * A copy of this chord carrying the given key context.\n *\n * Only a spelling the caller supplied (via `Chord.parse`, `Chord.of` with a\n * named root, or plain data carrying a hint) survives; a spelling that came\n * from a previously attached key is re-derived, so re-keying a progression\n * during a modulation does not keep the old key's letters and the order of\n * `withKey` calls does not affect the result.\n *\n * @param key The key context to attach.\n * @returns The new chord.\n */\n withKey(key: Key): Chord {\n return new Chord(this.#given, key);\n }\n\n /**\n * The chord's sorted, deduplicated pitch classes.\n *\n * A slash bass is one of them, because the chord sounds it.\n *\n * @param opts Set `includeBass: false` to enumerate the interval template\n * alone, leaving a slash bass out.\n * @returns Pitch classes ascending in [0, 11].\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('F/G').pitchClasses(); // [0, 5, 7, 9]\n * Chord.parse('F/G').pitchClasses({ includeBass: false }); // [0, 5, 9]\n * ```\n */\n pitchClasses(opts?: { includeBass?: boolean }): number[] {\n return chordPitchClasses(this.#data, opts);\n }\n\n /**\n * Whether a pitch is one of the chord's tones, ignoring octave.\n *\n * A slash bass counts, as it does in {@link Chord.pitchClasses}: the chord\n * sounds it.\n *\n * @param pitch A MIDI pitch or a bare pitch class.\n * @returns True when the pitch class belongs to the chord.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Cmaj7').contains(64); // true\n * Chord.parse('Cmaj7').contains(62); // false\n * ```\n */\n contains(pitch: number): boolean {\n return isChordMember(pitch, this.#data);\n }\n\n /**\n * A pitch's harmonic role in the chord: root, third, fifth, sixth, or\n * seventh.\n *\n * The role follows the pitch's interval above the root, so it answers for the\n * chord's own template rather than for the interval alone: the diminished\n * fifth of a half-diminished seventh is its fifth, while the same interval\n * over a chord that already has a perfect fifth is a `#11` tension and has no\n * basic role.\n *\n * @param pitch A MIDI pitch or a bare pitch class.\n * @returns The chord-tone role, or null when the pitch is a tension or a\n * foreign note.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Cmaj7').roleOf(67); // 'fifth'\n * Chord.parse('Cmaj7').roleOf(62); // null\n * ```\n */\n roleOf(pitch: number): ChordToneRole | null {\n return chordToneRole(pitch, this.#data);\n }\n\n /**\n * The chord's Roman numeral in a key.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @param opts Applied-numeral rendering options.\n * @returns The Roman numeral string.\n * @throws If no key is given and none is carried.\n */\n roman(key?: Key, opts?: ChordToRomanOptions): string {\n return chordToRoman(this.#data, this.#resolveKey(key).scale, opts);\n }\n\n /**\n * The chord's Roman numeral together with the reasoning behind it: the degree\n * its root was read as, the quality that set the numeral's case and suffix,\n * and what became of the bass.\n *\n * The numeral is exactly the one {@link Chord.roman} gives for the same\n * arguments, so this is that method with its rationale attached — for\n * teaching material, and for any interface whose users argue with the\n * analysis. Ask for `alternatives` to see the numerals the other option\n * settings would have rendered, and why this one was rendered instead.\n *\n * @param key Key to analyze in; a key name, a plain key/scale, or a\n * {@link Key}. Falls back to the carried context.\n * @param opts The rendering options {@link Chord.roman} takes, plus\n * `alternatives` to collect the readings this one turned down; see\n * {@link ExplainRomanOptions}.\n * @returns The numeral, its rationale, and the rejected readings.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, Key } from '@libraz/libcantus';\n * Chord.parse('G7').explain(Key.major('C')).roman; // 'V7'\n * Chord.parse('D7').explain('C major', { alternatives: true }).alternatives.length;\n * ```\n */\n explain(key?: KeyLike, opts?: ExplainRomanOptions): RomanExplanation {\n return explainRoman(this.#data, this.#resolveScale(key), opts);\n }\n\n /**\n * The chord's harmonic function (tonic / subdominant / dominant) in a key.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @returns The harmonic function.\n * @throws If no key is given and none is carried.\n */\n function(key?: Key): HarmonicFunction {\n return functionOf(this.#data, this.#resolveKey(key).scale);\n }\n\n /**\n * Full functional analysis: function, borrowing, Roman numeral, and the\n * rationale behind them.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @param opts Applied-numeral rendering options, plus `alternatives` to\n * collect the readings this analysis turned down; see\n * {@link AnalyzeChordOptions}.\n * @returns The chord analysis.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, Key } from '@libraz/libcantus';\n * Chord.parse('D7').analyze(Key.major('C'), { alternatives: true }).alternatives.length;\n * ```\n */\n analyze(key?: Key, opts?: AnalyzeChordOptions): ChordAnalysis {\n return analyzeChord(this.#data, this.#resolveKey(key).scale, opts);\n }\n\n /**\n * Whether the chord is borrowed from the parallel mode (modal interchange).\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @returns True if the chord is borrowed.\n * @throws If no key is given and none is carried.\n */\n isBorrowed(key?: Key): boolean {\n return isBorrowedChord(this.#data, this.#resolveKey(key).scale);\n }\n\n /**\n * The origin of a non-diatonic chord (parallel mode or Neapolitan), or null.\n *\n * @param key Key to analyze in; falls back to the carried context.\n * @returns The borrowing source, or null.\n * @throws If no key is given and none is carried.\n */\n borrowedSource(key?: Key): BorrowedSource {\n return borrowedSource(this.#data, this.#resolveKey(key).scale);\n }\n\n /**\n * The chord written as the figures a bass would carry under it: `6`, `64`,\n * `7`, `65`, `43`, `42`, or nothing at all for a root-position triad, with an\n * accidental on any interval the key does not already give.\n *\n * The bass the figures are measured above is the chord's own slash bass, or\n * its root in root position.\n *\n * @param key Key deciding which intervals need no accidental; a key name, a\n * plain key/scale, or a {@link Key}. Falls back to the carried context.\n * @returns The figures, as `realizeFiguredBass` reads them.\n * @throws If no key is given and none is carried, if the key is not\n * heptatonic, or if no figure names the chord — its bass is not one of its\n * tones, its tones do not stack in diatonic thirds above that bass, or it\n * is an added-tone or extended chord the notation has no abbreviation for.\n * @example\n * ```ts\n * import { Chord, Key } from '@libraz/libcantus';\n * Chord.parse('G/B').figuredBass(Key.major('C')); // '6'\n * Chord.parse('G7/D').figuredBass('C major'); // '43'\n * ```\n */\n figuredBass(key?: KeyLike): string {\n return figuredBassOf(this.#data, this.#resolveScale(key));\n }\n\n /**\n * The chords that can stand in for this one in a key: its tritone substitute,\n * the diatonic triads a third away that share two of its tones, the\n * parallel-mode chords with its harmonic function, and its chromatic\n * mediants.\n *\n * Each candidate carries the relationship it realizes, its Roman numeral, and\n * its harmonic function in the key.\n *\n * @param key Key the substitution is read in; a key name, a plain key/scale,\n * or a {@link Key}. Falls back to the carried context.\n * @param opts Set `melodyPcs` to the pitch classes a melody holds over this\n * chord, so only substitutions that keep every one of them a chord tone are\n * proposed.\n * @returns The deduplicated candidates; the chords are plain data, spelled\n * the way the key writes them.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, Key } from '@libraz/libcantus';\n * const subs = Chord.parse('G7').withKey(Key.major('C')).substitutions();\n * subs.find((sub) => sub.type === 'tritone')?.chord.rootPc; // 1\n * ```\n */\n substitutions(key?: KeyLike, opts?: SubstituteOptions): Substitution[] {\n return substituteChord(this.#data, this.#resolveScale(key), opts);\n }\n\n /**\n * The modal-interchange palette of the chord's key: the parallel mode's\n * triads that the key itself does not contain, plus the Neapolitan, each with\n * its Roman numeral and its borrowing source.\n *\n * The palette belongs to the key rather than to this chord, so it is the same\n * list for every chord in it; it is reachable here because a chord is where a\n * caller looking for somewhere else to go already is.\n *\n * @param key Key to borrow into; a key name, a plain key/scale, or a\n * {@link Key}. Falls back to the carried context.\n * @returns The borrowed chords, spelled the way the mode they come from\n * writes them.\n * @throws If no key is given and none is carried.\n * @example\n * ```ts\n * import { Chord, formatChordSymbol, Key } from '@libraz/libcantus';\n * const palette = Chord.parse('C').withKey(Key.major('C')).modalInterchange();\n * palette.map((borrowed) => formatChordSymbol(borrowed.chord));\n * // ['Cm', 'Ddim', 'Eb', 'Fm', 'Gm', 'Ab', 'Bb', 'Db']\n * ```\n */\n modalInterchange(key?: KeyLike): BorrowedChord[] {\n return modalInterchangePalette(this.#resolveScale(key));\n }\n\n /**\n * The chord rendered as a lead-sheet symbol (e.g. `'Cmaj7'`, `'F#m7'`, `'C/G'`).\n *\n * @param opts Set `flats: true` to spell the root/bass with flats, or\n * `system` to write them in another notation system.\n * @returns The chord symbol.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Bb7').symbol({ system: 'german' }); // 'B7'\n * ```\n */\n symbol(opts?: ChordSymbolOptions): string {\n return formatChordSymbol(this.#data, opts);\n }\n\n /**\n * Realize the chord as one MIDI pitch per voice, ascending.\n *\n * A chord that carries a key passes it to the voicer, so the leading tone is\n * not doubled; an explicit `opts.key` overrides it.\n *\n * @param opts Voicing options; defaults to four SATB voices.\n * @returns MIDI pitches, ascending, one per voice.\n * @throws If no voicing fits the given ranges.\n */\n voice(opts?: VoicingOptions): number[] {\n const key = opts?.key ?? this.#key?.scale;\n return voiceChord(this.#data, key === undefined ? opts : { ...opts, key });\n }\n\n /**\n * Realize the chord as a single styled voicing (`close`, `drop2`, `drop3`,\n * `shell`, or `rootless`), optionally constraining the top voice.\n *\n * @param opts Styled-voicing options; defaults to a close-position voicing.\n * @returns MIDI pitches, ascending.\n */\n styledVoicing(opts?: StyledVoicingOptions): number[] {\n return voiceChordStyled(this.#data, opts);\n }\n\n /**\n * The negative-harmony mirror of the chord about the key's tonic–dominant\n * axis (major becomes minor and vice versa).\n *\n * @param key Key providing the reflection axis; falls back to the carried\n * context.\n * @returns The mirrored chord, keeping any key context.\n * @throws If no key is given and none is carried.\n */\n negativeHarmony(key?: Key): Chord {\n const resolved = this.#resolveKey(key);\n // Retain the key that anchored the reflection (explicit first, then carried)\n // so a later no-arg analysis method still has a key context.\n return new Chord(negativeHarmonyMirror(this.#data, resolved.scale), key ?? this.#key);\n }\n\n /**\n * The V7 that tonicizes this chord: a dominant seventh a perfect fifth above\n * this chord's root.\n *\n * The target is the chord itself, so no key is involved and a borrowed or\n * chromatic chord gets its dominant as readily as a diatonic one. A root\n * spelling this chord supplied moves with the root, so `Eb` gives `Bb7`.\n *\n * @returns The secondary dominant, keeping any key context.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Eb').secondaryDominant().symbol(); // 'Bb7'\n * ```\n */\n secondaryDominant(): Chord {\n // #given, not #data: only a caller-supplied spelling is transposed, so a\n // spelling the key derived stays derived and follows the attached key.\n return new Chord(secondaryDominantOf(this.#given), this.#key);\n }\n\n /**\n * The n-th inversion: a copy whose bass is the chord tone `n` steps above the\n * root in the interval template (`invert(1)` puts the third in the bass).\n * `n` wraps around the template length; negative values count backwards.\n *\n * `invert(0)` (and any `n` that wraps to it) is root position, so it carries\n * no slash bass and equals the original chord.\n *\n * @param n The inversion number.\n * @returns The inverted chord, keeping any key context.\n * @throws If the chord has no intervals.\n */\n invert(n: number): Chord {\n const data = copyChord(this.#given);\n const intervals = data.intervals;\n const length = intervals.length;\n if (length === 0) {\n throw new InvalidInputError('cannot invert a chord with no intervals');\n }\n const index = ((n % length) + length) % length;\n // The bass spelling follows from the root spelling in force at read time, so\n // it is derived rather than frozen in here.\n delete data.bassSpelling;\n if (index === 0) {\n delete data.bassPc;\n } else {\n data.bassPc = mod12(data.rootPc + (intervals[index] ?? 0));\n }\n return new Chord(data, this.#key);\n }\n\n /**\n * Transpose the chord by a number of semitones.\n *\n * The quality and interval template are carried over, so a chord that a\n * symbol round-trip could not express — a custom interval set, an inversion —\n * survives. A carried key moves with the chord, so the transposed chord keeps\n * the same degree and function inside the transposed key.\n *\n * @param semitones The signed semitone offset.\n * @returns The transposed chord, in the transposed key when one is carried.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('C/G').transpose(2).symbol(); // 'D/A'\n * ```\n */\n transpose(semitones: number): Chord {\n const moved = transposeChord(this.#given, semitones);\n const rootSpelling = transposeHint(this.#given.rootSpelling, semitones);\n const bassSpelling = transposeHint(this.#given.bassSpelling, semitones);\n const toneSpellings = transposeToneHints(this.#given.toneSpellings, semitones);\n if (rootSpelling !== undefined) {\n moved.rootSpelling = rootSpelling;\n }\n if (bassSpelling !== undefined) {\n moved.bassSpelling = bassSpelling;\n }\n if (toneSpellings !== undefined) {\n moved.toneSpellings = toneSpellings;\n }\n return new Chord(moved, this.#key?.transpose(semitones));\n }\n\n /**\n * Transpose the chord by a spelled interval.\n *\n * Unlike {@link Chord.transpose}, which picks letters from a semitone count,\n * the interval's diatonic number decides them: a C chord up an augmented\n * fourth is F#, up a diminished fifth Gb. As with the semitone form, the\n * quality and interval template are carried over and a carried key moves with\n * the chord.\n *\n * @param interval An interval name (e.g. `'A4'`, `'-m3'`), plain interval\n * data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed chord, in the transposed key when one is carried.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('C').transposeBy('A4').symbol(); // 'F#'\n * Chord.parse('C').transposeBy('d5').symbol(); // 'Gb'\n * ```\n */\n transposeBy(interval: IntervalLike): Chord {\n const spelled = toSpelledInterval(interval);\n const moved = transposeChord(this.#given, spelled.semitones);\n // #given, not #data: only a caller-supplied spelling is transposed, so a\n // spelling the key derived stays derived and follows the transposed key.\n const rootSpelling = transposeHintByInterval(this.#given.rootSpelling, spelled);\n const bassSpelling = transposeHintByInterval(this.#given.bassSpelling, spelled);\n const toneSpellings = transposeToneHintsByInterval(this.#given.toneSpellings, spelled);\n if (rootSpelling !== undefined) {\n moved.rootSpelling = rootSpelling;\n }\n if (bassSpelling !== undefined) {\n moved.bassSpelling = bassSpelling;\n }\n if (toneSpellings !== undefined) {\n moved.toneSpellings = toneSpellings;\n }\n return new Chord(moved, this.#key?.transposeBy(spelled));\n }\n\n /**\n * The named scales that fit over this chord, best fit first, rooted on the\n * chord root.\n *\n * @returns The matching scales.\n */\n scales(): ChordScaleMatch[] {\n return chordScales(this.#data);\n }\n\n /**\n * The scales that fit over this chord, each with the tones it does not state\n * sorted by what may be done with them: the ones to avoid outright, the ones\n * a line may pass through, and the ones that may be added freely as color.\n *\n * {@link Chord.scales} ranks the same scales in the same order; this is that\n * ranking read as playing advice, so a caller does not have to pair it with\n * {@link Chord.avoidNotes} and {@link Chord.tensions} once per scale.\n *\n * @param limit Greatest number of scales to report; all of them by default.\n * @returns One entry per reported scale, best fit first; see\n * {@link ChordScaleReportEntry}.\n * @throws If `limit` is not a positive integer.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.of('C', 'maj7').scaleReport(1)[0]?.passing; // [5]\n * ```\n */\n scaleReport(limit?: number): ChordScaleReportEntry[] {\n return chordScaleReport(this.#data, limit);\n }\n\n /**\n * The available tensions (usable non-chord, non-avoid scale tones) of a\n * scale over this chord.\n *\n * @param scaleName A named scale, rooted on the chord root.\n * @param opts Set `resolvesTo` to the plain chord this one resolves to, so a\n * dominant resolving down a fifth onto a minor tonic takes the tensions\n * that resolution makes available; see {@link AvailableTensionsOptions}.\n * @returns Tension pitch classes, ascending in [0, 11].\n * @throws If `scaleName` is not a built-in scale.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.of('G', 'dom7').tensions('phrygianDominant', {\n * resolvesTo: Chord.of('C', 'min').data,\n * }); // [3, 8]\n * ```\n */\n tensions(scaleName: ScaleNameInput, opts?: AvailableTensionsOptions): number[] {\n return availableTensions(this.#data, scaleName, opts);\n }\n\n /**\n * The avoid notes (scale tones a semitone above a chord tone) of a scale\n * over this chord.\n *\n * @param scaleName A named scale, rooted on the chord root.\n * @param opts Set `use: 'melodic'` to judge a line rather than a voicing, so\n * a tone a line may pass through is not counted; see\n * {@link AvoidNotesOptions}.\n * @returns Avoid-note pitch classes, ascending in [0, 11].\n * @throws If `scaleName` is not a built-in scale.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.of('C', 'maj7').avoidNotes('ionian'); // [5]\n * Chord.of('C', 'maj7').avoidNotes('ionian', { use: 'melodic' }); // []\n * ```\n */\n avoidNotes(scaleName: ScaleNameInput, opts?: AvoidNotesOptions): number[] {\n return avoidNotes(this.#data, scaleName, opts);\n }\n\n /**\n * Spell the chord tones with letter names, root first, in the key's spelling.\n *\n * A chord that already knows how its root is spelled — one from\n * `Chord.parse`, or `Chord.of` with a named root — can spell itself with no\n * key at all; a key is only needed to choose a spelling for a bare pitch\n * class.\n *\n * A slash bass outside the chord tones is appended, so the spelled notes\n * are the notes the chord sounds — the same set {@link Chord.pitchClasses}\n * reports.\n *\n * @param key Key providing the spelled tonic; falls back to the carried\n * context, then to the chord's own root spelling.\n * @returns Spelled octave-less notes in the chord's own (tertian) order,\n * with any foreign slash bass last.\n * @throws If no key is given, none is carried, and the chord has no root\n * spelling of its own.\n */\n spell(key?: Key): Note[] {\n const resolved = key ?? this.#key;\n // An explicit key must select the spelling view before any derived hints\n // are read; only caller-provided hints in #given are independent of it.\n const data = this.#dataWithKey(resolved);\n let tones: NoteData[];\n if (resolved === undefined) {\n const root = data.rootSpelling;\n if (root === undefined) {\n throw new InvalidInputError(\n 'chord has no key context and no root spelling; pass a Key, attach one with withKey(), or build the chord from a symbol',\n );\n }\n tones = spellChordFromRoot(data, root);\n } else {\n tones = spellChord(data, resolved.tonic.data, resolved.scale);\n }\n const bassPc = data.bassPc;\n const bass = data.bassSpelling;\n if (\n bassPc !== undefined &&\n bass !== undefined &&\n !tones.some((tone) => mod12(noteToPitchClass(tone)) === mod12(bassPc))\n ) {\n tones = [...tones, { letter: bass.letter, alter: bass.alter }];\n }\n return tones.map((note) => new Note(note));\n }\n\n /**\n * Start a progression with this chord followed by others, carrying this\n * chord's key context (if any).\n *\n * @param others The chords following this one.\n * @returns The progression.\n */\n progressionTo(...others: Chord[]): Progression {\n return new Progression([this, ...others], this.#key);\n }\n\n /**\n * The chord placed at a beat, as the {@link ChordSpan} the arrangement and\n * generation functions take.\n *\n * The interval template is recorded only when it departs from the one the\n * quality names, so a standard chord yields the same span it always did while\n * a custom template is not lost. Key context and spelling hints are not part\n * of a span and are left behind.\n *\n * @param startBeat Beat the chord starts on.\n * @returns The span describing this chord at that beat.\n * @throws If `startBeat` is not finite, or the chord's quality is unknown.\n * @example\n * ```ts\n * import { Chord } from '@libraz/libcantus';\n * Chord.parse('Cmaj7').span(4); // { rootPc: 0, quality: 'maj7', startBeat: 4 }\n * ```\n */\n span(startBeat: number): ChordSpan {\n return spanFromChord(this.#data, startBeat);\n }\n\n /**\n * Whether another chord has the same root, quality, intervals, and bass.\n * Key context is not compared.\n *\n * @param other The chord to compare.\n * @returns True if the chord data is identical.\n */\n equals(other: Chord): boolean {\n const a = this.#data;\n const b = other.data;\n return (\n a.rootPc === b.rootPc &&\n a.quality === b.quality &&\n a.bassPc === b.bassPc &&\n a.intervals.length === b.intervals.length &&\n a.intervals.every((interval, i) => interval === b.intervals[i])\n );\n }\n\n /**\n * The plain chord data, for JSON serialization.\n *\n * @returns A copy of the underlying plain chord object.\n */\n toJSON(): ChordData {\n // Key context is intentionally not serialized. Persist only spelling hints\n // the caller provided, so a restored chord can be re-spelled by a new key.\n return copyChord(this.#given);\n }\n\n /**\n * The chord symbol, so a template literal or a log line reads as the chord.\n *\n * @returns The symbol, e.g. `'Cmaj7'`.\n */\n toString(): string {\n return this.symbol();\n }\n\n /**\n * Resolve the key/scale for a method that takes any key-shaped value:\n * explicit first, then carried, by the same rule {@link Chord.roman} follows.\n */\n #resolveScale(key?: KeyLike): KeyScale {\n return key === undefined ? this.#resolveKey().scale : toKeyScale(key);\n }\n\n /** Resolve the key for an analysis method: explicit first, then carried. */\n #resolveKey(key?: Key): Key {\n assertKeyArgument(key, 'chord key');\n const resolved = key ?? this.#key;\n if (resolved === undefined) {\n throw new InvalidInputError(\n 'chord has no key context; pass a Key or attach one with withKey()',\n );\n }\n return resolved;\n }\n}\n","import type { DetectKeyOptions, KeyMatch, KeyVariant } from '../analyze/detect/index.js';\nimport { detectKey, detectKeyBest } from '../analyze/detect/index.js';\nimport { isMinorKey, romanToChord } from '../analyze/functional/index.js';\nimport { InvalidInputError, type ParseResult, unwrapParse } from '../core/errors/index.js';\nimport type {\n IntervalLike,\n Note as NoteData,\n NoteLike,\n NoteNameOptions,\n SpelledInterval,\n} from '../core/pitch/index.js';\nimport {\n formatKeyName,\n spelledInterval,\n toNoteData,\n toSpelledInterval,\n transposeByInterval,\n tryParseKeyName,\n} from '../core/pitch/index.js';\nimport type { KeyScale } from '../core/types.js';\nimport { assertFiniteNumber, assertInteger, assertOneOf } from '../core/validation/index.js';\nimport {\n type ChordQuality,\n chordFromDegree,\n diatonicSeventh,\n diatonicTriad,\n} from '../theory/chord/index.js';\nimport {\n diatonicPitchClasses,\n dominantKeyOf,\n enharmonicKeyOf,\n isScaleTone,\n isSignatureKey,\n type KeyMode,\n type KeyRelation,\n keyFromFifths,\n keyRelationBetween,\n keySignatureFifths,\n majorKey,\n minorKey,\n NAMED_SCALES,\n nearestScaleTone,\n parallelKeyOf,\n pitchToScaleDegree,\n relatedKeysOf,\n relativeKeyOf,\n type ScaleName,\n type ScaleNameInput,\n type ScaleSystem,\n type SpelledKey,\n scaleByName,\n scaleSystemOf,\n scaleTonesInDegreeOrder,\n spelledKeyOf,\n subdominantKeyOf,\n supportsFunctionalHarmony,\n} from '../theory/scale/index.js';\nimport { spellPitchClasses, spellScale } from '../theory/spelling/index.js';\nimport type { TransposingInstrument } from '../theory/transposition/index.js';\nimport { toWrittenPitch } from '../theory/transposition/index.js';\nimport { Chord } from './chord.js';\nimport { Interval } from './interval.js';\nimport { Note } from './note.js';\nimport { Progression } from './progression.js';\nimport { mod12 } from './shared.js';\n\n/** A detected key paired with the score and scale form that produced it. */\nexport type DetectedKeyMatch = Omit<KeyMatch, 'key'> & { key: Key };\n\n/**\n * The plain form of a {@link Key}: the key/scale, the spelled tonic that\n * anchors its letter names, and the detected scale form when it has one.\n *\n * The variant travels with the data because a detected harmonic or melodic\n * minor is not recoverable from the scale mask and tonic alone, and a key that\n * loses it prints as a plain minor after a round trip through a project file.\n */\nexport type KeyData = { scale: KeyScale; tonic: NoteData; variant?: KeyVariant };\n\n/** Widest signature a key is actually written with, in fifths. */\nconst MAX_CONVENTIONAL_FIFTHS = 7;\n\n/** An alteration of two sharps or two flats: the spelling a tonic should avoid. */\nconst DOUBLE_ACCIDENTAL = 2;\n\n/** How many degrees a scale needs before thirds can be stacked on it. */\nconst HEPTATONIC_DEGREES = 7;\n\n/** The tonic a mode's reference scale is spelled on; C spells both modes plainly. */\nconst REFERENCE_TONIC: NoteData = { letter: 0, alter: 0 };\n\n/**\n * The interval between two tonics. Both are read octave-less, so the answer is\n * the ascending interval within one octave however the notes were built.\n */\nfunction bareInterval(from: Note, to: Note): SpelledInterval {\n return spelledInterval(\n { letter: from.letter, alter: from.alter },\n { letter: to.letter, alter: to.alter },\n );\n}\n\n/**\n * The tonic spelling a key arriving as a bare pitch class is written with.\n *\n * Routed through {@link spelledKeyOf}, the one place that answers this, so the\n * key a caller prints and the key the theory layer spells — a detection\n * rationale, a spelled line, a key region — never name the tonic differently.\n */\nfunction spelledTonicFor(scale: KeyScale): Note {\n return new Note(spelledKeyOf(scale).tonic);\n}\n\n/**\n * An immutable key/scale: a `KeyScale` (root pitch class plus mode mask) paired\n * with a spelled tonic that anchors letter-name spelling. Acts as the factory\n * for key-aware chords.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').chord(5).symbol(); // 'G' (the diatonic triad on scale degree 5)\n * ```\n */\n/**\n * Whether a value is the class API's {@link Note} rather than the plain note\n * data the pitch functions return.\n *\n * Read structurally rather than with `instanceof`: the package ships an ESM and\n * a CommonJS build, so a consumer reaching the two through different conditions\n * holds two `Note` classes, and an identity test would reject its own note.\n */\nfunction isNoteInstance(value: unknown): value is Note {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n const candidate = value as { pitchClass?: unknown; data?: unknown };\n return typeof candidate.pitchClass === 'number' && typeof candidate.data === 'object';\n}\n\n/** Name what arrived where the class API's {@link Note} was expected. */\nfunction describeTonic(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return String(value);\n }\n const candidate = value as Partial<NoteData>;\n return typeof candidate.letter === 'number' && typeof candidate.alter === 'number'\n ? 'plain note data'\n : 'an object that is not a note';\n}\n\n/**\n * An immutable key/scale: a `KeyScale` (root pitch class plus mode mask) paired\n * with a spelled tonic that anchors letter-name spelling. Acts as the factory\n * for key-aware chords.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').chord(5).symbol(); // 'G' (the diatonic triad on scale degree 5)\n * ```\n */\nexport class Key {\n readonly #scale: KeyScale;\n readonly #tonic: Note;\n readonly #variant: KeyVariant | undefined;\n\n /**\n * Wrap a key/scale and its spelled tonic.\n *\n * @param scale The key/scale; its root is normalized to a pitch class.\n * @param tonic The spelled tonic anchoring letter-name spelling.\n * @param variant Optional detected scale form retained for display.\n */\n constructor(scale: KeyScale, tonic: Note, variant?: KeyVariant) {\n assertInteger(scale.rootPc, 'scale.rootPc');\n assertInteger(scale.modeMask12, 'scale.modeMask12', 1, 0b111111111111);\n if ((scale.modeMask12 & 1) === 0) {\n throw new InvalidInputError('scale.modeMask12 must include the tonic (bit 0)');\n }\n // Named before the pitch class is read: a plain note from the pitch\n // functions has no `pitchClass`, so the comparison below would report the\n // mismatch of an `undefined` tonic instead of the type that arrived.\n if (!isNoteInstance(tonic)) {\n throw new InvalidInputError(\n `tonic must be the class API's Note; received ${describeTonic(tonic)}; ` +\n 'wrap plain note data with Note.fromData, or omit the tonic to have one chosen',\n );\n }\n const rootPc = mod12(scale.rootPc);\n if (tonic.pitchClass !== rootPc) {\n throw new InvalidInputError(\n `tonic ${tonic.name} does not match the scale root pitch class ${rootPc}; ` +\n 'pass a tonic that spells the scale root, or omit it to have one chosen',\n );\n }\n this.#scale = { rootPc, modeMask12: scale.modeMask12 };\n this.#tonic = tonic;\n this.#variant = variant;\n }\n\n /** Preserve detection metadata while replacing its plain key with this API's Key. */\n static #fromMatch(match: KeyMatch): DetectedKeyMatch {\n // The same spelling the detector's own rationale is written with, so a match\n // never names its tonic one way in `toString()` and another in `rationale`.\n const key = new Key(match.key, spelledTonicFor(match.key), match.variant);\n return { ...match, key };\n }\n\n /**\n * Wrap a key the theory layer spelled, keeping its tonic exactly as given.\n *\n * The relation functions work in fifths space, so the tonic they hand back is\n * already the one the key is written with; re-deriving it from the pitch class\n * would throw that spelling away (Bb minor would come back as A# minor).\n */\n static #fromSpelled(spelled: SpelledKey): Key {\n return new Key(spelled.key, new Note(spelled.tonic));\n }\n\n /**\n * A major key.\n *\n * @param root Tonic as a note name (e.g. `'Eb'`) or a pitch class; a numeric\n * root is spelled the way {@link spelledKeyOf} spells it — the side the\n * scale reads best from, which for a major key is the shorter signature.\n * @returns The major key.\n */\n static major(root: string | number): Key {\n if (typeof root === 'string') {\n const tonic = Note.parse(root);\n return new Key(majorKey(tonic.pitchClass), tonic);\n }\n const scale = majorKey(root);\n return new Key(scale, spelledTonicFor(scale));\n }\n\n /**\n * A natural-minor key.\n *\n * @param root Tonic as a note name or a pitch class; a numeric root is\n * spelled the way {@link spelledKeyOf} spells it, so pitch class 8 is G#\n * minor and not Ab minor.\n * @returns The minor key.\n */\n static minor(root: string | number): Key {\n if (typeof root === 'string') {\n const tonic = Note.parse(root);\n return new Key(minorKey(tonic.pitchClass), tonic);\n }\n const scale = minorKey(root);\n return new Key(scale, spelledTonicFor(scale));\n }\n\n /**\n * A key on a named scale (e.g. `'dorian'`, `'harmonicMinor'`).\n *\n * @param name The scale name, a key of the scale module's named-scale table.\n * @param root Tonic as a note name or a pitch class; a numeric root is\n * spelled the way {@link spelledKeyOf} spells it, exactly as\n * {@link Key.major} does — pitch class 1 altered is C#, not Db.\n * @returns The key.\n * @throws If the name is not a known scale.\n */\n static named(name: ScaleNameInput, root: string | number): Key {\n if (typeof root === 'string') {\n const tonic = Note.parse(root);\n return new Key(scaleByName(name, tonic.pitchClass), tonic);\n }\n const scale = scaleByName(name, mod12(root));\n return new Key(scale, spelledTonicFor(scale));\n }\n\n /**\n * Parse a key name, in any of the supported note-name systems.\n *\n * The name is a tonic and a mode word — `'C major'`, `'gis moll'`,\n * `'嬰ト短調'`, `'la minore'` — and the system is detected from the name\n * itself unless one is given. A German name may also be written with a\n * hyphen (`'gis-Moll'`), and its case carries the mode on its own, so `'Gis'`\n * is G sharp major and `'gis'` G sharp minor; see {@link parseKeyName} for\n * how a name whose case and mode word disagree is read.\n *\n * The result is always a plain major or minor key. Only the tonic's spelling\n * survives from the name, so `'ges dur'` is G flat major, not F sharp major.\n *\n * @param text The key name.\n * @param opts `system` reads the name in that notation system instead of\n * detecting it.\n * @returns The key.\n * @throws If the text is not a key name in the given (or detected) system,\n * or if it mixes two systems.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.parse('gis moll').toString(); // 'G# minor'\n * Key.parse('B dur').toString(); // 'Bb major' — the German B is a B flat\n * Key.parse('B major').toString(); // 'B major'\n * ```\n */\n static parse(text: string, opts?: NoteNameOptions): Key {\n return unwrapParse(Key.tryParse(text, opts));\n }\n\n /**\n * Parse a key name, reporting failure instead of throwing it.\n *\n * The same reading as {@link Key.parse}, for the callers where text that does\n * not name a key yet is the normal state of the input rather than a fault: a\n * key-signature field can say what is wrong with what has been typed so far\n * without a `try` around every keystroke.\n *\n * @param text The key name.\n * @param opts `system` reads the name in that notation system instead of\n * detecting it.\n * @returns The key, or the error explaining why the text is not one.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * const result = Key.tryParse('gis moll');\n * result.ok ? result.value.toString() : result.error.message; // 'G# minor'\n * ```\n */\n static tryParse(text: string, opts?: NoteNameOptions): ParseResult<Key> {\n const parsed = tryParseKeyName(text, opts);\n return parsed.ok\n ? { ok: true, value: Key.#modeOn(parsed.value.mode, new Note(parsed.value.tonic)) }\n : parsed;\n }\n\n /**\n * The key a key signature denotes: the inverse of {@link Key.fifths}.\n *\n * A signature names two keys — three sharps is both A major and F# minor — so\n * the mode decides which one is built. The tonic is spelled the way that key\n * is written, including the theoretical keys past ±7 (nine sharps is B#\n * major).\n *\n * @param fifths The signed number of sharps (positive) or flats (negative).\n * @param mode Which of the signature's two keys to build; defaults to major.\n * @returns The key.\n * @throws If `fifths` is not an integer in [-12, 12], or `mode` is neither\n * `'major'` nor `'minor'`.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.fromFifths(-2).toString(); // 'Bb major'\n * Key.fromFifths(-2, 'minor').toString(); // 'G minor'\n * ```\n */\n static fromFifths(fifths: number, mode: KeyMode = 'major'): Key {\n return Key.#fromSpelled(keyFromFifths(fifths, mode));\n }\n\n /**\n * Wrap an existing `KeyScale`, synthesizing a spelled tonic when none is\n * given.\n *\n * The synthesized tonic is chosen the same way as for a numeric root\n * elsewhere, by {@link spelledKeyOf}: the side the scale reads best from.\n * This is what keeps `Key.of(detectKey(...).scale)` from handing every\n * downstream chord a double-sharp spelling.\n *\n * @param scale The key/scale to wrap.\n * @param tonic Optional spelled tonic; must spell the scale's root pitch class.\n * @returns The key.\n * @throws If the given tonic is not the scale's root pitch class.\n */\n static of(scale: KeyScale, tonic?: Note): Key {\n return new Key(scale, tonic ?? spelledTonicFor(scale));\n }\n\n /**\n * Rebuild a key from its {@link Key.toJSON} output.\n *\n * @param data The serialized key, tonic, and scale form.\n * @returns The key.\n */\n static fromJSON(data: KeyData): Key {\n return new Key(data.scale, new Note(data.tonic), data.variant);\n }\n\n /**\n * Wrap plain key data, matching the `fromData` factory on the other classes.\n *\n * @param data The plain key, as {@link Key.data} hands it out.\n * @returns The key.\n */\n static fromData(data: KeyData): Key {\n return Key.fromJSON(data);\n }\n\n /**\n * Identify the keys a set of pitches fits, best interpretation first.\n *\n * The counterpart of {@link Chord.detect}.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to weigh the input; see {@link DetectKeyOptions}.\n * @returns Ranked keys (may be empty).\n */\n static detect(pitches: readonly number[], opts?: DetectKeyOptions): Key[] {\n return Key.detectMatches(pitches, opts).map((match) => match.key);\n }\n\n /**\n * Identify keys while retaining each candidate's score, fit, and scale form.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to weigh the input; see {@link DetectKeyOptions}.\n * @returns Ranked key matches (may be empty).\n */\n static detectMatches(pitches: readonly number[], opts?: DetectKeyOptions): DetectedKeyMatch[] {\n return detectKey(pitches, opts).map((match) => Key.#fromMatch(match));\n }\n\n /**\n * The single best key interpretation of a pitch set.\n *\n * @param pitches MIDI pitches or bare pitch classes.\n * @param opts How to weigh the input; see {@link DetectKeyOptions}.\n * @returns The top-ranked key, or null when nothing matches.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.detectBest([0, 2, 4, 5, 7, 9, 11])?.toString(); // 'C major'\n * ```\n */\n static detectBest(pitches: readonly number[], opts?: DetectKeyOptions): Key | null {\n const best = detectKeyBest(pitches, opts);\n return best === null ? null : Key.#fromMatch(best).key;\n }\n\n /**\n * Whether another key has the same tonic pitch class and mode.\n *\n * The spelled tonic is not compared: C# major and Db major are the same key\n * written two ways.\n *\n * @param other The key to compare.\n * @returns True when tonic and mode mask both match.\n */\n equals(other: Key): boolean {\n const scale = other.scale;\n return this.#scale.rootPc === scale.rootPc && this.#scale.modeMask12 === scale.modeMask12;\n }\n\n /** A copy of the underlying plain `KeyScale`. */\n get scale(): KeyScale {\n return { rootPc: this.#scale.rootPc, modeMask12: this.#scale.modeMask12 };\n }\n\n /** The spelled tonic. */\n get tonic(): Note {\n return this.#tonic;\n }\n\n /** The tonic pitch class (0..11). */\n get rootPc(): number {\n return this.#scale.rootPc;\n }\n\n /** Whether the scale has a minor third and no major third. */\n get isMinor(): boolean {\n return isMinorKey(this.#scale);\n }\n\n /**\n * The key signature as a signed count of sharps (positive) or flats\n * (negative) — the `fifths` value a notation format such as MusicXML writes.\n *\n * A scale that is not a diatonic mode takes the signature of its parallel\n * major or minor, so A harmonic minor reports 0: its G# is written as an\n * accidental rather than in the signature.\n *\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('Db').fifths; // -5\n * Key.minor('A').fifths; // 0\n * ```\n */\n get fifths(): number {\n return keySignatureFifths(this.#tonic.data, this.#scale);\n }\n\n /**\n * The built-in scale this key's mode mask is, or undefined when no built-in\n * scale has that mask.\n *\n * Only the Western vocabulary of {@link NAMED_SCALES} answers. A mask cannot\n * say which tradition names it — the major scale, maqam Ajam and thaat Bilaval\n * are the same seven pitch classes — so the scales of {@link WORLD_SCALES} are\n * reached by name rather than read back from one. Where two Western names\n * share a mask the table's own first name answers, so a natural minor reports\n * `'naturalMinor'` rather than `'aeolian'` and a major scale `'major'` rather\n * than `'ionian'`.\n *\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').scaleName; // 'major'\n * Key.minor('A').scaleName; // 'naturalMinor'\n * Key.named('dorian', 'D').scaleName; // 'dorian'\n * ```\n */\n get scaleName(): ScaleName | undefined {\n const mask = this.#scale.modeMask12;\n return (Object.keys(NAMED_SCALES) as ScaleName[]).find((name) => NAMED_SCALES[name] === mask);\n }\n\n /** A copy of the underlying plain key data. */\n get data(): KeyData {\n return this.toJSON();\n }\n\n /** The detected scale form, when this key came from key detection. */\n get variant(): KeyVariant | undefined {\n return this.#variant;\n }\n\n /**\n * The scale's pitch classes.\n *\n * They come in scale-degree order by default — the tonic first, then each\n * scale tone above it — so D dorian starts on 2 and wraps past 11 to 0. Ask\n * for `order: 'ascending'` where the answer is a pitch-class set rather than\n * a scale: sorted numerically, it compares directly against what\n * {@link Chord.pitchClasses} reports and against any pitch-class set of the\n * caller's own.\n *\n * @param opts Set `order: 'ascending'` to sort the pitch classes numerically\n * instead of by scale degree.\n * @returns One pitch class per scale degree.\n * @throws If `order` is neither `'degree'` nor `'ascending'`.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.named('dorian', 'D').pitchClasses(); // [2, 4, 5, 7, 9, 11, 0]\n * Key.named('dorian', 'D').pitchClasses({ order: 'ascending' }); // [0, 2, 4, 5, 7, 9, 11]\n * ```\n */\n pitchClasses(opts?: { order?: 'degree' | 'ascending' }): number[] {\n const order =\n opts?.order === undefined\n ? 'degree'\n : assertOneOf(opts.order, ['degree', 'ascending'], 'pitch-class order');\n return order === 'ascending'\n ? diatonicPitchClasses(this.#scale)\n : scaleTonesInDegreeOrder(this.#scale);\n }\n\n /**\n * The kind of pitch organisation this key's scale belongs to: the\n * common-practice material functional harmony is defined on, a modal\n * rotation of it, or a collection that carries no chord function of its own.\n *\n * A key holds a mode mask rather than a tradition, so the mask is read the\n * Western way, exactly as {@link scaleSystemOf} reads a bare `KeyScale`: a\n * key built on maqam Hijaz answers `'modal'`, because those seven pitch\n * classes are also the phrygian dominant. A mask no built-in scale names has\n * no system at all.\n *\n * @returns The scale system, or undefined for a mask that names no built-in\n * scale.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').system(); // 'common-practice'\n * Key.named('dorian', 'D').system(); // 'modal'\n * Key.named('majorPentatonic', 'C').system(); // 'non-functional'\n * ```\n */\n system(): ScaleSystem | undefined {\n return scaleSystemOf(this.#scale);\n }\n\n /**\n * Whether functional (Roman-numeral) analysis describes this key.\n *\n * True for the common-practice and modal systems and false for everything\n * classed as carrying no chord function, so code about to read a pentatonic\n * or a raga as a chord progression can ask here first instead of imposing\n * degrees on it. The mask is read the Western way, as {@link Key.system}\n * reads it.\n *\n * @returns True when functional harmony describes the scale.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.named('harmonicMinor', 'A').supportsFunctionalHarmony(); // true\n * Key.named('majorPentatonic', 'C').supportsFunctionalHarmony(); // false\n * ```\n */\n supportsFunctionalHarmony(): boolean {\n return supportsFunctionalHarmony(this.#scale);\n }\n\n /**\n * The relative key: the same key signature read in the other mode.\n *\n * Because the new tonic is read off the circle of fifths rather than\n * transposed by semitones, the relative of Db major is Bb minor and not its\n * enharmonic A# minor.\n *\n * @returns The relative key, always a plain major or minor key.\n * @throws If this key's signature falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').relative().toString(); // 'A minor'\n * ```\n */\n relative(): Key {\n return Key.#fromSpelled(relativeKeyOf(this.#tonic.data, this.#scale));\n }\n\n /**\n * The parallel key: the other mode on the same tonic, which keeps this key's\n * tonic spelling — the parallel minor of C major is C minor, never B# minor.\n *\n * @returns The parallel key, always a plain major or minor key.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').parallel().toString(); // 'C minor'\n * ```\n */\n parallel(): Key {\n return Key.#fromSpelled(parallelKeyOf(this.#tonic.data, this.#scale));\n }\n\n /**\n * The dominant key: the key a fifth above, in the mode this key's third names.\n *\n * The fifth is measured from the tonic, so a key that is not a plain major or\n * minor answers with the same move: the dominant of D dorian is A minor.\n *\n * @returns The key a fifth above.\n * @throws If the resulting signature falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').dominantKey().toString(); // 'G major'\n * ```\n */\n dominantKey(): Key {\n return Key.#fromSpelled(dominantKeyOf(this.#tonic.data, this.#scale));\n }\n\n /**\n * The subdominant key: the key a fifth below, in the mode this key's third\n * names, and the exact inverse of {@link Key.dominantKey} on the tonic\n * spelling.\n *\n * @returns The key a fifth below.\n * @throws If the resulting signature falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').subdominantKey().toString(); // 'F major'\n * ```\n */\n subdominantKey(): Key {\n return Key.#fromSpelled(subdominantKeyOf(this.#tonic.data, this.#scale));\n }\n\n /**\n * The same sounding key written the other way round the circle of fifths.\n *\n * Only a spelling within ±7 fifths is offered, because that is as far as keys\n * are actually written: Db major answers with C# major, while C major has no\n * second spelling at all.\n *\n * @returns The other spelling of this key, or null when it has none.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('Db').enharmonic()?.toString(); // 'C# major'\n * Key.major('C').enharmonic(); // null\n * ```\n */\n enharmonic(): Key | null {\n const other = enharmonicKeyOf(this.#tonic.data, this.#scale);\n return other === null ? null : Key.#fromSpelled(other);\n }\n\n /**\n * The six closely related keys, each tagged with its relation.\n *\n * These are the keys the German and Japanese teaching tradition counts as a\n * key's near relations: the relative and parallel keys, the dominant and the\n * subdominant, and the relatives of those two. Five of them are written\n * within one accidental of this key's signature — the relative shares it\n * exactly and the other four stand one away; the parallel key stands three\n * away and belongs to the set for the tonic it shares instead.\n *\n * @returns The related keys in relation order.\n * @throws If a neighbouring signature falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C')\n * .relatedKeys()\n * .map((related) => `${related.relation}: ${related.key}`);\n * // ['relative: A minor', 'parallel: C minor', 'dominant: G major', ...]\n * ```\n */\n relatedKeys(): { relation: KeyRelation; key: Key }[] {\n return relatedKeysOf(this.#tonic.data, this.#scale).map(({ relation, ...spelled }) => ({\n relation,\n key: Key.#fromSpelled(spelled),\n }));\n }\n\n /**\n * How another key stands to this one.\n *\n * Only the identity test looks at the tonic spelling; every other relation\n * compares tonic pitch class and mode, so C# minor and Db minor both read as\n * the relative of E major. Both keys are read through the mode their third\n * names, so a detected A harmonic minor stands to C major exactly as A minor\n * does, and the answer reads the same from either side.\n *\n * @param other The key the relation is measured to.\n * @returns The relation, or null when `other` is none of this key's related\n * keys.\n * @throws If a neighbouring signature of this key falls outside [-12, 12]\n * fifths.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').relationTo(Key.minor('A')); // 'relative'\n * Key.major('C').relationTo(Key.minor('Eb')); // null\n * ```\n */\n relationTo(other: Key): KeyRelation | null {\n // The other key is read through its public accessors, not its private\n // fields, so a bundle that emits two copies of this class still compares.\n return keyRelationBetween(\n { tonic: this.#tonic.data, key: this.#scale },\n { tonic: other.tonic.data, key: other.scale },\n );\n }\n\n /**\n * A scale degree, counted from 1 the way musicians name degrees: `degree(1)`\n * is the tonic and `degree(5)` the dominant.\n *\n * @param n The 1-based scale degree.\n * @returns The spelled note on that degree.\n * @throws If `n` is not an integer within 1..(number of scale degrees) — seven\n * for a diatonic key, five for a pentatonic one.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.minor('A').degree(4).name; // 'D'\n * ```\n */\n degree(n: number): Note {\n const notes = this.notes();\n assertInteger(n, 'degree', 1, notes.length);\n // The bounds check already guarantees the index; the tonic only satisfies\n // the compiler's view of an indexed read.\n return notes[n - 1] ?? this.#tonic;\n }\n\n /**\n * The key rooted on one of this key's scale degrees.\n *\n * Without an explicit `mode` the mode is read off the diatonic triad on that\n * degree: a major triad gives a major key, a minor or diminished one a minor\n * key. So the fourth degree of A minor gives D minor while the fourth degree\n * of C major gives F major. A scale with no diatonic triads — anything that\n * is not heptatonic — falls back to this key's own mode.\n *\n * @param n The 1-based scale degree, as {@link Key.degree} counts them.\n * @param mode The mode of the resulting key; inferred when omitted.\n * @returns The key on that degree.\n * @throws If `n` is outside the scale's degrees.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.minor('A').keyOnDegree(4).toString(); // 'D minor'\n * Key.minor('A').keyOnDegree(4, 'major').toString(); // 'D major'\n * ```\n */\n keyOnDegree(n: number, mode?: KeyMode): Key {\n const tonic = this.degree(n);\n return Key.#modeOn(mode ?? this.#modeOfDegree(n), tonic);\n }\n\n /**\n * The key on whose degree `n` this key's tonic sits: the inverse of\n * {@link Key.keyOnDegree}.\n *\n * D minor is the fourth degree of A minor, so D minor answers A minor here.\n *\n * @param n The 1-based degree this key's tonic should occupy in the result.\n * @param mode The mode of the resulting key; defaults to this key's own mode.\n * @returns The key that has this key's tonic on its `n`th degree.\n * @throws If `n` is not an integer in 1..7 — the result is a major or minor\n * key, which has seven degrees.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.minor('D').keyHavingTonicAsDegree(4).toString(); // 'A minor'\n * ```\n */\n keyHavingTonicAsDegree(n: number, mode?: KeyMode): Key {\n const resolved = mode ?? (this.isMinor ? 'minor' : 'major');\n // The interval from degree 1 to degree n is a property of the mode, not of\n // the tonic, so it is read off the mode's scale built on C and then applied\n // downward to this tonic — which keeps the letter the interval names.\n const reference = spellScale(REFERENCE_TONIC, resolved === 'minor' ? minorKey(0) : majorKey(0));\n assertInteger(n, 'degree', 1, reference.length);\n const first = reference[0] as NoteData;\n const step = spelledInterval(first, reference[n - 1] as NoteData);\n const tonic = new Note(\n transposeByInterval(this.#tonic.data, {\n number: step.number,\n quality: step.quality,\n // Negating a zero span yields -0, which compares unequal to 0.\n semitones: step.semitones === 0 ? 0 : -step.semitones,\n descending: true,\n }),\n );\n return Key.#modeOn(resolved, tonic);\n }\n\n /** A plain major or minor key on a spelled tonic. */\n static #modeOn(mode: KeyMode, tonic: Note): Key {\n const rootPc = tonic.pitchClass;\n return new Key(mode === 'minor' ? minorKey(rootPc) : majorKey(rootPc), tonic);\n }\n\n /**\n * The mode a scale degree implies, read off its diatonic triad. Scales that\n * have no diatonic triads are tested by their degree count rather than by\n * letting {@link diatonicTriad} throw.\n */\n #modeOfDegree(n: number): KeyMode {\n if (this.pitchClasses().length !== HEPTATONIC_DEGREES) {\n return this.isMinor ? 'minor' : 'major';\n }\n const { quality } = diatonicTriad(n, this.#scale);\n return quality === 'min' || quality === 'dim' ? 'minor' : 'major';\n }\n\n /**\n * Transpose the key by a spelled interval, keeping the spelling the interval\n * names.\n *\n * Unlike {@link Key.transpose}, which picks a letter from the semitone count,\n * the interval's diatonic number decides the tonic's letter: G# minor down an\n * augmented fourth is D minor, while down a diminished fifth it is Dbb minor.\n * The mode mask is unchanged, so the scale keeps its shape.\n *\n * @param interval The interval to apply, as a name (`'A4'`, `'-A4'`), plain\n * interval data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed key.\n * @throws If the value does not describe a spelled interval.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.minor('D').transposeBy('A4').toString(); // 'G# minor'\n * Key.minor('G#').transposeBy('-A4').toString(); // 'D minor'\n * ```\n */\n transposeBy(interval: IntervalLike): Key {\n const step = toSpelledInterval(interval);\n return new Key(\n { rootPc: mod12(this.#scale.rootPc + step.semitones), modeMask12: this.#scale.modeMask12 },\n new Note(transposeByInterval(this.#tonic.data, step)),\n this.#variant,\n );\n }\n\n /**\n * The spelled interval from this key's tonic to another key's tonic.\n *\n * Both tonics are octave-less, so the interval is measured within one\n * ascending octave: C major to Eb major is a minor third, and Eb major back\n * to C major is a major sixth.\n *\n * @param other The key to measure to.\n * @returns The interval between the two tonics.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').intervalTo(Key.major('F#')).name; // 'A4'\n * ```\n */\n intervalTo(other: Key): Interval {\n return Interval.fromData(bareInterval(this.#tonic, other.tonic));\n }\n\n /**\n * Transpose the key by a number of semitones.\n *\n * The mode mask is unchanged, so the scale keeps its shape; only the tonic\n * moves. The tonic's letter follows the semitone count, and the result is\n * then respelled to its enharmonic key whenever the key would not be written\n * that way: Db major up a semitone reads as D major rather than as Ebb major\n * and its ten flats. A key written with a signature is judged by that\n * signature — anything within ±7 is left exactly as it is, so C major up six\n * semitones stays F# major — and a scale that only borrows one is judged by\n * whether its spelling needs a double accidental. A key whose enharmonic is\n * unwritable too keeps the letter-transposed spelling.\n *\n * Use {@link Key.transposeBy} to transpose by a named interval instead, which\n * spells the tonic exactly as that interval demands.\n *\n * @param semitones The signed semitone offset.\n * @returns The transposed key.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').transpose(2).toString(); // 'D major'\n * Key.major('Db').transpose(1).toString(); // 'D major'\n * ```\n */\n transpose(semitones: number): Key {\n assertFiniteNumber(semitones, 'semitones');\n const rootPc = mod12(this.#scale.rootPc + Math.round(semitones));\n const scale = { rootPc, modeMask12: this.#scale.modeMask12 };\n const moved = new Key(scale, this.#tonic.transpose(semitones), this.#variant);\n if (moved.#isWritten()) {\n return moved;\n }\n // The enharmonic key supplies the tonic spelling only: the scale keeps this\n // key's own mask, so a harmonic minor or a pentatonic is respelled rather\n // than flattened into a plain major or minor key. A respelling that does not\n // sound the same root is no respelling at all, so the moved key stands —\n // transposing is total, and no spelling question may turn it into a throw.\n const other = enharmonicKeyOf(moved.tonic.data, moved.scale);\n const respelled = other === null ? null : new Note(other.tonic);\n return respelled === null || respelled.pitchClass !== rootPc\n ? moved\n : new Key(scale, respelled, this.#variant);\n }\n\n /**\n * Whether this key is spelled the way keys are actually written, which is\n * what decides whether {@link Key.transpose} leaves a spelling alone.\n *\n * A key with a signature of its own is written wherever that signature is,\n * up to seven sharps or flats. A scale that only borrows the signature of its\n * parallel major or minor is written wherever it reads: the borrowed count\n * says nothing — the octatonic scale on Db borrows eight flats and is spelled\n * Db all the same — so what counts there is that no note needs a double\n * accidental.\n */\n #isWritten(): boolean {\n return isSignatureKey(this.#scale)\n ? Math.abs(this.fifths) <= MAX_CONVENTIONAL_FIFTHS\n : this.notes().every((note) => Math.abs(note.alter) < DOUBLE_ACCIDENTAL);\n }\n\n /**\n * The key a transposing instrument's part is written in, for this key at\n * concert pitch.\n *\n * The direction is written-side: the part is transposed *away* from what the\n * instrument sounds, so a B flat instrument — which sounds a major second\n * lower than it reads — has its part written a major second higher, and a\n * concert C major becomes D major. The tonic is spelled by the interval, so\n * a concert E flat major reads as F major on that instrument rather than as\n * E sharp major, and the mode mask is untouched.\n *\n * The opposite reading, a written key back to the key it sounds in, is\n * {@link Key.transposeBy} applied to {@link instrumentTransposition} — the\n * instrument's own written-to-sounding interval.\n *\n * @param instrument A built-in instrument name, or an interval naming a\n * transposition the table does not carry.\n * @returns The key the player reads.\n * @throws If the instrument is neither a known name nor a spelled interval.\n * @example\n * ```ts\n * import { Key, instrumentTransposition } from '@libraz/libcantus';\n * Key.major('C').forInstrument('clarinetBb').toString(); // 'D major'\n * Key.major('C').forInstrument('hornF').toString(); // 'G major'\n * // And back: what a part written in C major on a clarinet in A sounds as.\n * Key.major('C').transposeBy(instrumentTransposition('clarinetA')).toString(); // 'A major'\n * ```\n */\n forInstrument(instrument: TransposingInstrument): Key {\n // Routed through the same conversion the notes use, so a key and the notes\n // of its part can never disagree about the direction or the spelling.\n const tonic = new Note(toWrittenPitch(this.#tonic.data, instrument));\n return new Key(\n { rootPc: tonic.pitchClass, modeMask12: this.#scale.modeMask12 },\n tonic,\n this.#variant,\n );\n }\n\n /**\n * The spelled scale, one note per degree (e.g. C D E F G A B for C major).\n *\n * @returns Spelled octave-less notes in scale-degree order.\n */\n notes(): Note[] {\n return spellScale(this.#tonic.data, this.#scale).map((note) => new Note(note));\n }\n\n /**\n * Alias of {@link Key.notes}.\n *\n * @returns Spelled octave-less notes in scale-degree order.\n */\n spell(): Note[] {\n return this.notes();\n }\n\n /**\n * The spelled scale as note-name strings.\n *\n * @param opts `system` writes the names in that notation system instead of\n * English, matching {@link Key.toString}.\n * @returns One name per scale degree.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').noteNames(); // ['C', 'D', 'E', 'F', 'G', 'A', 'B']\n * Key.minor('G#').noteNames({ system: 'german' })[0]; // 'gis'\n * ```\n */\n noteNames(opts?: NoteNameOptions): string[] {\n return this.notes().map((note) => note.format(opts));\n }\n\n /**\n * Spell arbitrary pitch classes the way this key writes them.\n *\n * The counterpart of {@link Key.notes} for pitches that are not scale\n * degrees: a detected pitch-class set, an analysis result, or a voicing\n * reduced to pitch classes gets the letters and accidentals the key implies,\n * so in F major pitch class 10 reads as Bb while 11 reads as B natural\n * rather than as Cb.\n *\n * Named for what it takes, because {@link Key.spell} already answers the\n * scale itself.\n *\n * @param pcs The pitch classes, spelled in the order they are given.\n * @param tonic Spelled tonic anchoring the letter names, as a note name, a\n * MIDI number, or a {@link Note}; defaults to this key's own tonic. It has\n * to sound this key's root pitch class, so it can only respell that tonic —\n * a Db major key spelled from C#.\n * @returns Spelled octave-less notes, in input order.\n * @throws If `tonic` does not sound this key's root pitch class.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('F')\n * .spellPitchClasses([10, 11])\n * .map((note) => note.name); // ['Bb', 'B']\n * ```\n */\n spellPitchClasses(pcs: readonly number[], tonic?: NoteLike): Note[] {\n const anchor = tonic === undefined ? this.#tonic.data : toNoteData(tonic);\n return spellPitchClasses([...pcs], anchor, this.#scale).map((note) => new Note(note));\n }\n\n /**\n * Build a chord on a scale degree, carrying this key as context.\n *\n * With an explicit quality the quality's interval template is attached to the\n * degree's diatonic root; without one the scale-correct diatonic triad is\n * stacked (e.g. a diminished triad on the leading tone of a major key).\n *\n * @param degree 1-based scale degree of the chord root, as {@link Key.degree}\n * counts them: 1 is the tonic and 5 the dominant.\n * @param quality Optional chord quality.\n * @returns The chord, with this key attached.\n * @throws Without a `quality`, if this key's scale is not heptatonic —\n * stacking thirds needs seven degrees, so the pentatonic, blues,\n * whole-tone, octatonic and chromatic scales have no diatonic triad. Pass\n * an explicit `quality` for those.\n */\n chord(degree: number, quality?: ChordQuality): Chord {\n const data =\n quality === undefined\n ? diatonicTriad(degree, this.#scale)\n : chordFromDegree(degree, quality, this.#scale);\n return new Chord(data, this);\n }\n\n /**\n * The diatonic triad on a scale degree, carrying this key as context.\n *\n * @param degree 1-based scale degree of the chord root, as {@link Key.degree}\n * counts them: 1 is the tonic and 5 the dominant.\n * @returns The triad, with this key attached.\n * @throws If this key's scale is not heptatonic; stacking thirds needs seven\n * degrees. Use {@link Key.chord} with an explicit quality instead.\n */\n diatonicTriad(degree: number): Chord {\n return new Chord(diatonicTriad(degree, this.#scale), this);\n }\n\n /**\n * The diatonic seventh chord on a scale degree, carrying this key as context.\n *\n * @param degree 1-based scale degree of the chord root, as {@link Key.degree}\n * counts them: 1 is the tonic and 5 the dominant.\n * @returns The seventh chord, with this key attached.\n * @throws If this key's scale is not heptatonic; stacking thirds needs seven\n * degrees. Use {@link Key.chord} with an explicit quality instead.\n */\n diatonicSeventh(degree: number): Chord {\n return new Chord(diatonicSeventh(degree, this.#scale), this);\n }\n\n /**\n * Build the chord denoted by a Roman numeral in this key (including applied\n * chords such as `'V7/V'`), carrying this key as context.\n *\n * @param text The Roman numeral.\n * @returns The chord, with this key attached.\n * @throws If the numeral is not valid.\n */\n roman(text: string): Chord {\n return new Chord(romanToChord(text, this.#scale), this);\n }\n\n /**\n * Build a progression from Roman numerals in this key.\n *\n * Each numeral is read as {@link Key.roman} reads it, applied chords included,\n * and the progression carries this key, so it can name its own numerals,\n * functions and cadences without being handed a key again.\n *\n * @param romans The numerals, in order.\n * @returns The progression, carrying this key.\n * @throws If any numeral is not valid.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * const progression = Key.major('C').progression('I', 'vi', 'IV', 'V');\n * progression.toString(); // 'C Am F G'\n * progression.functions(); // ['tonic', 'tonic', 'subdominant', 'dominant']\n * ```\n */\n progression(...romans: string[]): Progression {\n return new Progression(\n romans.map((text) => this.roman(text)),\n this,\n );\n }\n\n /**\n * Whether a pitch belongs to the scale.\n *\n * @param x A MIDI pitch, bare pitch class, or note.\n * @returns True if the pitch class is a scale tone.\n */\n contains(x: number | Note): boolean {\n return isScaleTone(typeof x === 'number' ? x : x.pitchClass, this.#scale);\n }\n\n /**\n * The nearest MIDI pitch whose pitch class is in the scale.\n *\n * The search expands symmetrically outward from `pitch`, and a tie — an equal\n * distance above and below — is settled downward. A pitch already in the scale\n * answers itself.\n *\n * @param pitch The MIDI pitch to snap.\n * @returns The nearest in-scale MIDI pitch.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').nearestTone(61); // 60\n * Key.major('C').nearestTone(64); // 64\n * ```\n */\n nearestTone(pitch: number): number {\n return nearestScaleTone(pitch, this.#scale);\n }\n\n /**\n * The scale degree a pitch sits on, counted from 1: the tonic is degree 1.\n *\n * A pitch outside the scale has no degree and answers null, so a caller\n * reading a degree cannot mistake the absent answer for the tonic.\n *\n * @param pitch A MIDI pitch or a bare pitch class.\n * @returns The 1-based degree, or null when the pitch is not a scale tone.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.major('C').degreeOf(64); // 3\n * Key.major('C').degreeOf(61); // null\n * ```\n */\n degreeOf(pitch: number): number | null {\n const degree = pitchToScaleDegree(pitch, this.#scale);\n return degree === -1 ? null : degree;\n }\n\n /**\n * The plain key data, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(key)` from collapsing to `{}`. The result pairs the\n * `KeyScale` with the spelled tonic and, for a detected harmonic or melodic\n * minor, the scale form — everything {@link Key.fromJSON} needs to rebuild a\n * key that reads and prints as this one.\n *\n * @returns The key/scale, its spelled tonic, and its scale form when it has\n * one.\n */\n toJSON(): KeyData {\n // The variant is omitted rather than written as undefined, so a key that\n // never came from detection serializes to the same object it always did.\n return this.#variant === undefined\n ? { scale: this.scale, tonic: this.#tonic.data }\n : { scale: this.scale, tonic: this.#tonic.data, variant: this.#variant };\n }\n\n /**\n * The key's tonic and mode, so a template literal or a log line reads as the\n * key. Detected harmonic and melodic minor keys retain their scale form.\n *\n * A `system` names the key the way that notation system writes it, including\n * German's case convention. The scale form is an English-only qualifier —\n * the other systems have no word for it — so a detected harmonic minor names\n * its parallel plain minor there.\n *\n * @param opts `system` writes the name in that notation system instead of\n * English.\n * @returns The name, e.g. `'C major'`, `'A minor'` or `'gis moll'`.\n * @example\n * ```ts\n * import { Key } from '@libraz/libcantus';\n * Key.minor('G#').toString(); // 'G# minor'\n * Key.minor('G#').toString({ system: 'german' }); // 'gis moll'\n * Key.minor('G#').toString({ system: 'japanese' }); // '嬰ト短調'\n * ```\n */\n toString(opts?: NoteNameOptions): string {\n if (opts?.system !== undefined && opts.system !== 'english') {\n return formatKeyName(\n { tonic: this.#tonic.data, mode: this.isMinor ? 'minor' : 'major' },\n opts,\n );\n }\n if (this.#variant === 'harmonic' || this.#variant === 'melodic') {\n return `${this.#tonic.name} ${this.#variant} minor`;\n }\n return `${this.#tonic.name} ${this.isMinor ? 'minor' : 'major'}`;\n }\n}\n","import type { DetectKeyOptions, KeyMatch } from '../analyze/detect/index.js';\nimport { detectKeyFromNotes } from '../analyze/detect/index.js';\nimport type {\n FormSection,\n FormSectionOptions,\n Hypermeter,\n HypermeterOptions,\n Phrase,\n PhraseOptions,\n StructuralCadence,\n} from '../analyze/form/index.js';\nimport {\n hypermeter,\n phrasesFromTimeline,\n sectionsFromNotes,\n structuralCadences,\n} from '../analyze/form/index.js';\nimport type { KeyRegion, KeyTimelineOptions } from '../analyze/keys/index.js';\nimport { keyLookup, keyTimelineFromNotes, prevailingKeyOf } from '../analyze/keys/index.js';\nimport type { ExtractMotifsOptions, MelodicContour, MotifData } from '../analyze/melody/index.js';\nimport { extractMotifs, melodicContour } from '../analyze/melody/index.js';\nimport type { ChordTimeline, ChordTimelineOptions } from '../analyze/timeline/index.js';\nimport { chordTimelineFromNotes } from '../analyze/timeline/index.js';\nimport type { AnalyzedNote, IdentifiedVoiceNote, KeyContext } from '../analyze/voice/index.js';\nimport { analyzeVoice, toVoiceNotes } from '../analyze/voice/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { NoteEventIndex, NoteEventIndexOptions } from '../core/event-index/index.js';\nimport { createNoteEventIndex } from '../core/event-index/index.js';\nimport type { PlayabilityReport } from '../core/instrument/playability.js';\nimport { playability } from '../core/instrument/playability.js';\nimport type { InstrumentProfile, InstrumentProfileLike } from '../core/instrument/profile.js';\nimport { toInstrumentProfile } from '../core/instrument/profile.js';\nimport type { BarPosition, MeterLike, MeterMap, TimeSignature } from '../core/meter/index.js';\nimport { beatToBarPosition, meterAt, resolveMeters, toMeterData } from '../core/meter/index.js';\nimport type { IntervalLike } from '../core/pitch/index.js';\nimport { toSpelledInterval } from '../core/pitch/index.js';\nimport type { TempoMap } from '../core/tempo/index.js';\nimport { beatsToSeconds, beatsToTicks, tempoAt, ticksToBeats } from '../core/tempo/index.js';\nimport type { KeyScale, NoteEvent } from '../core/types.js';\nimport { assertFiniteNumber, assertNoteEvent, assertRange } from '../core/validation/index.js';\nimport type { GrooveTemplate, HumanizeOptions, OrnamentOptions } from '../generate/index.js';\nimport {\n applyGrooveTemplate,\n extractGrooveTemplate,\n humanize,\n ornament,\n} from '../generate/index.js';\nimport { type KeyLike, toKeyScale } from '../theory/scale/index.js';\nimport type { KeyData } from './key.js';\nimport { Key } from './key.js';\nimport { copyNoteEvent, withoutNegativeZero } from './shared.js';\nimport { Timeline } from './timeline.js';\n\n/** The plain form a {@link Score} hands out and is rebuilt from. */\nexport type ScoreData = {\n notes: NoteEvent[];\n meters: MeterMap;\n tempo: TempoMap;\n /**\n * The key the notes are read in, when one was given. Absent on a score whose\n * key is left to be inferred, so the two states survive a round trip: a score\n * that carries no key is not one that carries C major.\n */\n key?: KeyData;\n};\n\n/** How a score is built: the notes, plus the context they are read against. */\nexport type ScoreOptions = {\n /** The meter, as one time signature — `'6/8'` or its data — or a map of changes. */\n meters?: MeterLike;\n /** The tempo, as one bpm or a map of changes. */\n tempo?: TempoMap | number;\n /** The key the notes are read in, when it is already known. */\n key?: KeyLike;\n};\n\n/** The tempo a score is read at when the caller names none. */\nconst DEFAULT_BPM = 120;\n\n/**\n * The notes checked and copied, in time order, ties broken by pitch.\n *\n * A score is asked about beats, not about array positions, so the order is\n * settled once here: every method downstream — and every `equals` between two\n * scores holding the same music — then reads the same sequence whatever order\n * the notes arrived in.\n *\n * Each note is checked by the shared note-event guard as it is copied, since\n * copying is already note by note. Zero-length notes are accepted, as they are\n * everywhere on the analysis side: a MIDI import carries them, and the analyses\n * drop them themselves.\n */\nfunction orderNotes(notes: readonly NoteEvent[], name: string): NoteEvent[] {\n if (!Array.isArray(notes)) {\n throw new InvalidInputError(`${name} must be an array; received ${typeof notes}`);\n }\n return notes\n .map((note, index) => {\n if (note === undefined) {\n throw new InvalidInputError(`${name}[${index}] must be a note event; received undefined`);\n }\n return copyNoteEvent(\n assertNoteEvent(note, `${name}[${index}]`, { allowNonPositiveDuration: true }),\n );\n })\n .sort(\n (a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch || a.durationBeat - b.durationBeat,\n );\n}\n\n/** Defensive copy of a meter map, with its signatures copied entry by entry. */\nfunction copyMeters(meters: MeterMap): MeterMap {\n return meters.map((change) => {\n const ts: TimeSignature = {\n numerator: change.ts.numerator,\n denominator: change.ts.denominator,\n };\n if (change.ts.grouping !== undefined) {\n ts.grouping = [...change.ts.grouping];\n }\n return { startBeat: withoutNegativeZero(change.startBeat), ts };\n });\n}\n\n/**\n * Defensive copy of a tempo map, validated on the way through.\n *\n * The tempo module keeps its map validator private and runs it from every\n * entry point, so the map is checked by reading the tempo at its own origin:\n * a non-finite bpm, an unsorted entry or an empty map is refused here rather\n * than at the first conversion a caller happens to ask for.\n */\nfunction copyTempo(tempo: TempoMap): TempoMap {\n tempoAt(0, tempo);\n return tempo.map((event) => ({\n startBeat: withoutNegativeZero(event.startBeat),\n bpm: event.bpm,\n }));\n}\n\n/**\n * Defensive copy of plain score data, with every part validated.\n *\n * This is the boundary a project file, a MIDI import or a hand-edited JSON\n * crosses, so the checks are the library's own: the note events, the meter map\n * and the tempo map are each rejected here if they carry a number the analysis\n * below could not hold.\n */\nfunction copyScore(data: ScoreData): ScoreData {\n const copy: ScoreData = {\n notes: orderNotes(data.notes, 'score notes'),\n // The map is validated by the same resolver every meter-aware entry point\n // reads its options through, so a score accepts exactly what they accept.\n meters: copyMeters(resolveMeters({ meters: data.meters }, 'score meters')),\n tempo: copyTempo(data.tempo),\n };\n if (data.key !== undefined) {\n copy.key = Key.fromJSON(data.key).toJSON();\n }\n return copy;\n}\n\n/** The meter map a {@link ScoreOptions.meters} value names. */\nfunction metersFrom(meters: MeterLike | undefined): MeterMap {\n if (meters === undefined) {\n return resolveMeters({}, 'score meters');\n }\n const meter = toMeterData(meters, 'score meters');\n return Array.isArray(meter)\n ? resolveMeters({ meters: meter }, 'score meters')\n : resolveMeters({ ts: meter }, 'score meters');\n}\n\n/** The tempo map a {@link ScoreOptions.tempo} value names. */\nfunction tempoFrom(tempo: TempoMap | number | undefined): TempoMap {\n if (tempo === undefined) {\n return [{ startBeat: 0, bpm: DEFAULT_BPM }];\n }\n return typeof tempo === 'number' ? [{ startBeat: 0, bpm: tempo }] : tempo;\n}\n\n/** The plain data a set of notes and a context describe. */\nfunction scoreData(notes: readonly NoteEvent[], opts: ScoreOptions | undefined): ScoreData {\n const data: ScoreData = {\n notes: [...notes],\n meters: metersFrom(opts?.meters),\n tempo: tempoFrom(opts?.tempo),\n };\n if (opts?.key !== undefined) {\n data.key = Key.of(toKeyScale(opts.key)).toJSON();\n }\n return data;\n}\n\n/**\n * Sounding notes with the context that gives them meaning: a meter map, a\n * tempo map, and optionally the key they are read in.\n *\n * This is the library's entry point for music that already exists. A DAW\n * plug-in, a transcription tool, or a MIDI importer holds `NoteEvent[]` before\n * it holds anything else, and every question worth asking of it — what chords\n * are these, where do the phrases end, what key is this — took a separate\n * function and a hand-wired chain of intermediate results. A `Score` carries\n * the notes and their context together, so each question is one call and the\n * answers stay in the class API.\n *\n * The notes are held in time order, and their onsets are unbounded below: beat\n * 0 is the first downbeat, so a pickup sounds at a negative beat and is kept\n * there rather than being shifted onto the grid.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const score = Score.of([\n * { pitch: 60, startBeat: 0, durationBeat: 2 },\n * { pitch: 64, startBeat: 0, durationBeat: 2 },\n * { pitch: 67, startBeat: 0, durationBeat: 2 },\n * ]);\n * score.totalBeats; // 2\n * ```\n */\nexport class Score {\n readonly #data: ScoreData;\n readonly #key: Key | undefined;\n\n /**\n * Wrap plain score data.\n *\n * @param data The notes and their context; copied, never retained.\n * @throws If a note, the meter map or the tempo map carries a value the\n * analysis cannot hold.\n */\n constructor(data: ScoreData) {\n this.#data = copyScore(data);\n this.#key = this.#data.key === undefined ? undefined : Key.fromJSON(this.#data.key);\n }\n\n /**\n * Build a score from note events.\n *\n * A bare time signature becomes a one-entry meter map taking effect at beat\n * 0, and a bare bpm a one-entry tempo map at beat 0; a score given neither is\n * read in 4/4 at 120 bpm, which is what every meter-aware function in the\n * library assumes when its caller names nothing.\n *\n * @param notes The sounding notes, in any order.\n * @param opts The meter, tempo, and key to read them against.\n * @returns The score.\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const score = Score.of([{ pitch: 60, startBeat: 0, durationBeat: 4 }], {\n * meters: { numerator: 3, denominator: 4 },\n * tempo: 90,\n * });\n * score.meterAt(0).numerator; // 3\n * ```\n */\n static of(notes: readonly NoteEvent[], opts?: ScoreOptions): Score {\n return new Score(scoreData(notes, opts));\n }\n\n /** An empty score, for a track that has not been written yet. */\n static empty(opts?: ScoreOptions): Score {\n return new Score(scoreData([], opts));\n }\n\n /**\n * Read note events whose times are in ticks rather than beats.\n *\n * Onsets and durations are converted; nothing else about the events changes.\n * A negative tick count is a pickup and stays one, since beat 0 is the first\n * downbeat at either resolution.\n *\n * @param events The events, with tick-valued onsets and durations.\n * @param ppq Ticks per quarter note.\n * @param opts The meter, tempo, and key to read them against.\n * @returns The score, in beats.\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const score = Score.fromTicks([{ pitch: 60, startBeat: 480, durationBeat: 960 }], 480);\n * score.notes[0]?.durationBeat; // 2\n * ```\n */\n static fromTicks(events: readonly NoteEvent[], ppq: number, opts?: ScoreOptions): Score {\n const notes = events.map((event) => ({\n ...event,\n startBeat: ticksToBeats(event.startBeat, ppq),\n durationBeat: ticksToBeats(event.durationBeat, ppq),\n }));\n return new Score(scoreData(notes, opts));\n }\n\n /** Rebuild a score from the plain data {@link Score.data} hands out. */\n static fromData(data: ScoreData): Score {\n return new Score(data);\n }\n\n /** Rebuild a score from its {@link Score.toJSON} output. */\n static fromJSON(data: ScoreData): Score {\n return new Score(data);\n }\n\n /** The sounding notes, in time order. */\n get notes(): readonly NoteEvent[] {\n return this.#data.notes.map(copyNoteEvent);\n }\n\n /**\n * Where the last note stops sounding.\n *\n * Measured from beat 0 rather than from the first onset, so a score that\n * opens with a pickup reports the beat its music ends on and not its length.\n * An empty score ends at 0.\n */\n get totalBeats(): number {\n return this.#data.notes.reduce(\n (end, note) => Math.max(end, note.startBeat + note.durationBeat),\n 0,\n );\n }\n\n /** The meter map the notes are read against. */\n get meters(): MeterMap {\n return copyMeters(this.#data.meters);\n }\n\n /** The tempo map the notes are read against. */\n get tempo(): TempoMap {\n return this.#data.tempo.map((event) => ({ startBeat: event.startBeat, bpm: event.bpm }));\n }\n\n /**\n * The stretch of the score between two beats.\n *\n * A note is kept when its onset falls in `[fromBeat, toBeat)`; the notes keep\n * their absolute onsets, so the slice stays in the score's own time and a\n * slice taken across a meter or tempo change is still read against it.\n *\n * @param fromBeat First beat of the stretch, inclusive.\n * @param toBeat End of the stretch, exclusive.\n * @returns The score holding those notes.\n */\n slice(fromBeat: number, toBeat: number): Score {\n assertFiniteNumber(fromBeat, 'slice fromBeat');\n assertFiniteNumber(toBeat, 'slice toBeat');\n return this.#withNotes(\n this.#data.notes.filter((note) => note.startBeat >= fromBeat && note.startBeat < toBeat),\n );\n }\n\n /** The notes this predicate keeps, with the same context. */\n filter(predicate: (note: NoteEvent, index: number) => boolean): Score {\n return this.#withNotes(\n this.#data.notes.filter((note, index) => predicate(copyNoteEvent(note), index)),\n );\n }\n\n /** The notes this function returns, with the same context. */\n map(fn: (note: NoteEvent, index: number) => NoteEvent): Score {\n return this.#withNotes(this.#data.notes.map((note, index) => fn(copyNoteEvent(note), index)));\n }\n\n /** This score followed by another's notes. */\n concat(other: Score | readonly NoteEvent[]): Score {\n // Read through the public surface rather than by `instanceof`, so a score\n // built by a second copy of the module concatenates like any other.\n const added: readonly NoteEvent[] = Array.isArray(other)\n ? (other as readonly NoteEvent[])\n : (other as Score).notes;\n return this.#withNotes([...this.#data.notes, ...added]);\n }\n\n /**\n * The score moved along the timeline.\n *\n * The meter and tempo maps stay where they are: they describe the bars and\n * the pulse the music is played against, and moving the notes over them is\n * how a phrase is heard a bar later. A negative shift moves music before the\n * first downbeat, which is where a pickup lives.\n *\n * @param beats How far to move, in quarter-note beats; negative moves earlier.\n * @returns The moved score.\n */\n shift(beats: number): Score {\n assertFiniteNumber(beats, 'shift beats');\n return this.#withNotes(\n this.#data.notes.map((note) => ({ ...note, startBeat: note.startBeat + beats })),\n );\n }\n\n /**\n * The score moved by a number of semitones.\n *\n * @param semitones The signed semitone offset.\n * @returns The transposed score.\n * @throws If a note would leave the MIDI range.\n */\n transpose(semitones: number): Score {\n assertFiniteNumber(semitones, 'transpose semitones');\n return this.#withNotes(\n this.#data.notes.map((note) => ({ ...note, pitch: note.pitch + semitones })),\n );\n }\n\n /**\n * The score moved by a spelled interval, keeping the spelling.\n *\n * Note events carry a MIDI pitch and no spelling of their own, so the\n * interval contributes its size and its direction; a carried key is what\n * keeps the spelling, and it moves with the notes.\n *\n * @param interval An interval name (e.g. `'A4'`, `'-m3'`), plain interval\n * data, or an {@link Interval}; a descending interval moves down.\n * @returns The transposed score.\n */\n transposeBy(interval: IntervalLike): Score {\n return this.transpose(toSpelledInterval(interval).semitones);\n }\n\n /** The same notes read against a different meter. */\n withMeters(meters: MeterLike): Score {\n return new Score({ ...this.#data, meters: metersFrom(meters) });\n }\n\n /** The same notes read against a different tempo. */\n withTempo(tempo: TempoMap | number): Score {\n return new Score({ ...this.#data, tempo: tempoFrom(tempo) });\n }\n\n /** The same notes read in a different key. */\n withKey(key: KeyLike): Score {\n return new Score({ ...this.#data, key: Key.of(toKeyScale(key)).toJSON() });\n }\n\n /**\n * The notes pulled onto a grid.\n *\n * Onsets and durations are both snapped to the nearest multiple of the grid,\n * counted from beat 0 in both directions, so a pickup is quantized against\n * the same grid the downbeat is. A duration that would round to nothing keeps\n * one grid unit, since a note quantized out of existence is a note lost.\n *\n * @param grid The grid unit in quarter-note beats (0.25 for sixteenths).\n * @returns The quantized score.\n * @throws If the grid is not a positive length.\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const score = Score.of([{ pitch: 60, startBeat: 0.98, durationBeat: 1.03 }]);\n * score.quantize(0.5).notes[0]?.startBeat; // 1\n * ```\n */\n quantize(grid: number): Score {\n assertRange(grid, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'quantize grid');\n return this.#withNotes(\n this.#data.notes.map((note) => ({\n ...note,\n startBeat: Math.round(note.startBeat / grid) * grid,\n durationBeat: Math.max(1, Math.round(note.durationBeat / grid)) * grid,\n })),\n );\n }\n\n /**\n * The notes pushed off the grid the way a player would.\n *\n * The metric accents are read from the score's opening signature, since\n * {@link HumanizeOptions} names one signature rather than a map; pass `ts`\n * to read a later stretch of a score that changes meter.\n *\n * @param opts Jitter amounts, accent depth and the seed; see\n * {@link HumanizeOptions}.\n * @returns The humanized score.\n */\n humanize(opts?: HumanizeOptions): Score {\n return this.#withNotes(humanize(this.#data.notes, { ts: this.meterAt(0), ...opts }));\n }\n\n /**\n * The notes with ornaments added.\n *\n * Strong positions are read from the score's opening signature, since\n * {@link OrnamentOptions} names one signature rather than a map.\n *\n * @param opts The ornament style, how much is decorated, and the seed; see\n * {@link OrnamentOptions}.\n * @returns The ornamented score.\n */\n ornament(opts?: OrnamentOptions): Score {\n return this.#withNotes(ornament(this.#data.notes, { ts: this.meterAt(0), ...opts }));\n }\n\n /**\n * The notes placed on a groove template's slots.\n *\n * The template's per-bar grid is laid out against the score's opening\n * signature, which is the signature the template must have been extracted\n * under; a template carrying a different one is refused rather than drifting.\n *\n * @param template The groove template, from `extractGrooveTemplate`.\n * @returns The score with the template's feel imposed.\n * @throws If the template was extracted under another meter.\n */\n groove(template: GrooveTemplate): Score {\n return this.#withNotes(applyGrooveTemplate(this.#data.notes, template, this.meterAt(0)));\n }\n\n /**\n * The feel of the score's own timing, as a template another score can take on.\n *\n * The other direction of {@link Score.groove}: a played-in take is measured\n * slot by slot for how far ahead of or behind the grid it sits and how hard\n * it is struck, and the reading is handed back as a template. Only the\n * subdivision is named, because the score already holds the meter the grid is\n * laid out against — its opening signature, which is the one the template\n * records and the one an apply-time meter is checked against.\n *\n * @param subdivision Grid steps per quarter-note beat; a sixteenth-note grid\n * by default, which is where swing, push and drag are audible.\n * @returns The template these notes describe.\n * @throws If the subdivision is not a positive whole number.\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const played = Score.of([{ pitch: 36, startBeat: 0.02, durationBeat: 1 }]);\n * Score.of([{ pitch: 36, startBeat: 0, durationBeat: 1 }]).groove(played.grooveTemplate());\n * ```\n */\n grooveTemplate(subdivision?: number): GrooveTemplate {\n return extractGrooveTemplate(this.#data.notes, this.meterAt(0), subdivision);\n }\n\n /**\n * The harmony the notes spell out, in time.\n *\n * The score's meter map and its key, when it carries one, are read against\n * unless the options name others.\n *\n * @param opts Analysis options; see {@link ChordTimelineOptions}.\n * @returns The inferred timeline, carrying the key regions the analysis found\n * and how sure it is of each segment.\n */\n timeline(opts?: ChordTimelineOptions): Timeline {\n const result = chordTimelineFromNotes(this.#data.notes, this.#analysisOptions(opts));\n return Timeline.fromData({\n segments: result.timeline.segments,\n totalBeats: opts?.totalBeats ?? this.totalBeats,\n keys: result.keys,\n segmentConfidence: result.segmentConfidence,\n });\n }\n\n /**\n * The key held longest across the score, when one can be read.\n *\n * A score carrying a key answers with it. Otherwise the key regions are\n * searched for and the one holding for the most beats wins, so a piece that\n * digresses is named by the key it keeps coming back to rather than by the\n * key it opens on. A score with nothing sounding has no key at all.\n *\n * @returns The prevailing key, or undefined when nothing sounds.\n */\n key(): Key | undefined {\n if (this.#key !== undefined) {\n return this.#key;\n }\n const prevailing = prevailingKeyOf(this.#keyRegions());\n return prevailing === null ? undefined : Key.of(prevailing);\n }\n\n /**\n * Every key region the score passes through, in time order.\n *\n * @param opts Analysis options; see {@link KeyTimelineOptions}.\n * @returns The regions; empty when nothing sounds.\n */\n keys(opts?: KeyTimelineOptions): KeyRegion[] {\n return keyTimelineFromNotes(this.#data.notes, { meters: this.#data.meters, ...opts });\n }\n\n /**\n * Every key the score could be in, ranked, read as one key across the whole\n * of it.\n *\n * {@link Score.keys} divides the score into the regions it passes through;\n * this ranks the readings of the score entire, so a caller can see what the\n * winner beat and by how much. Each note counts for its duration times its\n * velocity — the measure chord inference already weighs by — so the sustained\n * harmony that establishes a key outweighs a run of ornaments over it, which\n * is what makes a score the right thing to ask rather than a bare histogram\n * of its pitches.\n *\n * @param opts Which profile to rank with, whether the church modes take part,\n * whether each candidate explains itself, and the budget; the weights come\n * from the notes themselves, so they are not on offer.\n * @returns The candidates, best first; empty when nothing sounds.\n * @example\n * ```ts\n * import { Score } from '@libraz/libcantus';\n * const score = Score.of([{ pitch: 60, startBeat: 0, durationBeat: 4 }]);\n * score.detectKeys()[0]?.key.rootPc; // 0\n * ```\n */\n detectKeys(opts?: Omit<DetectKeyOptions, 'weights'>): KeyMatch[] {\n return detectKeyFromNotes(this.#data.notes, opts);\n }\n\n /**\n * The phrases the notes fall into.\n *\n * The cadences are read from the score's own harmony, so the chord timeline\n * does not have to be built and passed in by hand.\n *\n * @param opts Analysis options; see {@link PhraseOptions}.\n * @returns The phrases, in time order.\n */\n phrases(opts?: PhraseOptions): Phrase[] {\n return phrasesFromTimeline(this.#chordTimeline(), this.#data.notes, {\n meters: this.#data.meters,\n ...(this.#key === undefined ? {} : { key: this.#key.scale }),\n ...opts,\n });\n }\n\n /**\n * The cadences that close the score's phrases, ranked by how much structure\n * each one closes.\n *\n * {@link Score.phrases} says where the phrases end; this says which of those\n * endings matter. Two chords alone cannot tell the close of a piece from a\n * passing confirmation halfway through a hyperbar, and a phrase reading can,\n * because it knows what the cadence closes and where that sits. Phrases no\n * cadence closes are left out.\n *\n * @param opts How the phrases are read; see {@link PhraseOptions}.\n * @returns The cadences, heaviest first; ties keep the earlier one.\n */\n structuralCadences(opts?: PhraseOptions): StructuralCadence[] {\n return structuralCadences(this.phrases(opts));\n }\n\n /**\n * The sections the score divides into.\n *\n * @param opts Analysis options; see {@link FormSectionOptions}.\n * @returns The sections, in time order.\n */\n sections(opts?: FormSectionOptions): FormSection[] {\n return sectionsFromNotes(this.#data.notes, { meters: this.#data.meters, ...opts });\n }\n\n /**\n * The bar-level pulse above the meter.\n *\n * @param opts Analysis options; see {@link HypermeterOptions}.\n * @returns The grouping, its downbeats, a confidence, and a rationale.\n */\n hypermeter(opts?: HypermeterOptions): Hypermeter {\n return hypermeter(this.#data.notes, this.#data.meters, opts);\n }\n\n /**\n * The motifs the melody repeats.\n *\n * The plain analysis record comes back rather than a {@link Motif}, as it\n * does from every other reader here that is not {@link Score.timeline} or\n * {@link Score.key}: a `Motif` holds a bare cell of notes, so wrapping the\n * record would drop the intervals, the rhythmic profile, every later\n * occurrence and the rationale — the findings the extraction was asked for.\n * A record is a cell, so `Motif.fromData(found)` reaches the\n * transformations without anything being rebuilt by hand.\n *\n * @param opts Cell-length bounds and the recurrence threshold; see\n * {@link ExtractMotifsOptions}.\n * @returns The motifs found, longest first.\n * @example\n * ```ts\n * import { Motif, Score } from '@libraz/libcantus';\n * const score = Score.of([\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * { pitch: 60, startBeat: 3, durationBeat: 1 },\n * { pitch: 62, startBeat: 4, durationBeat: 1 },\n * { pitch: 64, startBeat: 5, durationBeat: 1 },\n * ]);\n * const found = score.motifs()[0];\n * found?.occurrences.length; // 2\n * found === undefined ? undefined : Motif.fromData(found).notes.length; // 3\n * ```\n */\n motifs(opts?: ExtractMotifsOptions): MotifData[] {\n return extractMotifs(this.#data.notes, opts);\n }\n\n /** The shape the melody traces. */\n contour(): MelodicContour {\n return melodicContour(this.#data.notes);\n }\n\n /**\n * Each note's role in the harmony sounding under it.\n *\n * The chord under each note and the key it is read in both come from the\n * score: the harmony from its own chord timeline, and the key from the\n * argument, then the carried key, then the regions the analysis found. A\n * score with no readable harmony — nothing sounding, or too little to name a\n * chord — reads every note against C major and no chord at all, which labels\n * the notes as the non-chord tones they are rather than failing.\n *\n * @param key The key to read the notes in; defaults to the score's own.\n * @returns One annotation per note, in the score's own time order.\n */\n voices(key?: KeyLike): AnalyzedNote[] {\n const timeline = this.#chordTimeline();\n return analyzeVoice(this.#data.notes, timeline.at, this.#keyContext(key));\n }\n\n /**\n * The notes carrying the ids the voice analysis reports them back under.\n *\n * {@link Score.voices} answers about the notes; this hands back the notes\n * themselves with the handle each answer names, so a caller pairing an\n * annotation or a conflict with the note it describes has something to pair\n * it with. The id is the note's position in the score's own time order, which\n * is not necessarily the order the notes arrived in.\n *\n * @returns One voice note per note, in the score's own time order.\n */\n voiceNotes(): IdentifiedVoiceNote[] {\n return toVoiceNotes(this.#data.notes);\n }\n\n /**\n * Whether an instrument can play the score, and what makes it hard.\n *\n * The tempo the third layer needs is the score's own, read at beat 0.\n *\n * @param instrument The instrument to play it on: a plain\n * {@link InstrumentProfile} or an {@link Instrument}.\n * @returns Difficulty, the issues found, and each note's placement.\n * @throws If the value names no instrument, or the instrument it names is\n * contradictory.\n * @example\n * ```ts\n * import { Instrument, Score } from '@libraz/libcantus';\n * const score = Score.of([{ pitch: 27, startBeat: 0, durationBeat: 1 }]);\n * score.playability(Instrument.bass4()).issues[0]?.type; // 'noteOutOfRange'\n * ```\n */\n playability(instrument: InstrumentProfileLike): PlayabilityReport {\n return playability(\n this.#data.notes,\n toInstrumentProfile(instrument),\n tempoAt(0, this.#data.tempo),\n );\n }\n\n /**\n * An index for repeated lookups over the notes.\n *\n * @param opts How the events are read; see {@link NoteEventIndexOptions}.\n * @returns The index.\n */\n index(opts?: NoteEventIndexOptions): NoteEventIndex {\n return createNoteEventIndex(this.#data.notes, {\n allowNonPositiveDuration: true,\n name: 'score notes',\n ...opts,\n });\n }\n\n /**\n * Where a beat falls in seconds, read through the tempo map.\n *\n * Every tempo segment the span crosses is integrated, so a beat after a\n * tempo change is not the change's tempo applied to the whole span. A beat\n * inside a pickup reports negative elapsed time, at the opening tempo.\n *\n * @param beat Position in quarter-note beats.\n * @returns Elapsed seconds from the tempo map's origin.\n */\n secondsAt(beat: number): number {\n return beatsToSeconds(beat, this.#data.tempo);\n }\n\n /**\n * Where a beat falls in bars, read through the meter map.\n *\n * @param beat Position in quarter-note beats.\n * @returns The 0-based bar and the quarter-note offset inside it; a pickup\n * reports bar -1.\n */\n barAt(beat: number): BarPosition {\n return beatToBarPosition(beat, this.#data.meters);\n }\n\n /**\n * The time signature sounding at a beat.\n *\n * @param beat Position in quarter-note beats.\n * @returns The signature in force there.\n */\n meterAt(beat: number): TimeSignature {\n return meterAt(beat, this.#data.meters);\n }\n\n /**\n * The score's beats as MIDI ticks, for a writer that counts in them.\n *\n * @param ppq Ticks per quarter note.\n * @returns The notes with tick-valued onsets and durations, in time order.\n */\n toTicks(ppq: number): NoteEvent[] {\n return this.#data.notes.map((note) => ({\n ...note,\n startBeat: beatsToTicks(note.startBeat, ppq),\n durationBeat: beatsToTicks(note.durationBeat, ppq),\n }));\n }\n\n /**\n * Whether another score holds the same notes in the same context.\n *\n * The comparison is made through the other score's public data, so two\n * scores built by different copies of the module still compare.\n *\n * @param other The score to compare.\n * @returns True when the notes, the meter, the tempo and the read key match.\n */\n equals(other: Score): boolean {\n const theirs = other.data;\n const mine = this.#data;\n return (\n mine.notes.length === theirs.notes.length &&\n mine.notes.every((note, index) => sameNote(note, theirs.notes[index])) &&\n mine.meters.length === theirs.meters.length &&\n mine.meters.every((change, index) => sameMeter(change, theirs.meters[index])) &&\n mine.tempo.length === theirs.tempo.length &&\n mine.tempo.every((event, index) => {\n const theirEvent = theirs.tempo[index];\n return (\n theirEvent !== undefined &&\n event.startBeat === theirEvent.startBeat &&\n event.bpm === theirEvent.bpm\n );\n }) &&\n sameKey(mine.key, theirs.key)\n );\n }\n\n /** A copy of the underlying plain data. */\n get data(): ScoreData {\n const copy: ScoreData = {\n notes: this.#data.notes.map(copyNoteEvent),\n meters: copyMeters(this.#data.meters),\n tempo: this.#data.tempo.map((event) => ({ startBeat: event.startBeat, bpm: event.bpm })),\n };\n if (this.#key !== undefined) {\n copy.key = this.#key.toJSON();\n }\n return copy;\n }\n\n /** The plain form of the score, for `JSON.stringify`. */\n toJSON(): ScoreData {\n return this.data;\n }\n\n /** The same context carrying another set of notes. */\n #withNotes(notes: readonly NoteEvent[]): Score {\n return new Score({ ...this.#data, notes: [...notes] });\n }\n\n /**\n * The chord timeline the score's own harmony describes.\n *\n * Private, because the options belong to the members that expose them: the\n * analyses that read harmony read the score's, and a caller who wants\n * another builds it through {@link Score.timeline}.\n */\n #chordTimeline(): ChordTimeline {\n return chordTimelineFromNotes(this.#data.notes, this.#analysisOptions()).timeline;\n }\n\n /** The key regions under the score, read against its own meter. */\n #keyRegions(): KeyRegion[] {\n return keyTimelineFromNotes(this.#data.notes, { meters: this.#data.meters });\n }\n\n /** The score's context as chord-timeline options, with the caller's on top. */\n #analysisOptions(opts?: ChordTimelineOptions): ChordTimelineOptions {\n return {\n meters: this.#data.meters,\n ...(this.#key === undefined ? {} : { key: this.#key.scale }),\n ...opts,\n };\n }\n\n /**\n * The key each note is read against: the argument, then the carried key,\n * then the key in force at the beat, and C major where nothing sounds.\n */\n #keyContext(key: KeyLike | undefined): KeyContext {\n if (key !== undefined) {\n return toKeyScale(key);\n }\n if (this.#key !== undefined) {\n return this.#key.scale;\n }\n const regions = this.#keyRegions();\n const prevailing: KeyScale = prevailingKeyOf(regions) ?? Key.major('C').scale;\n return keyLookup(regions, prevailing);\n }\n}\n\n/** Whether two plain note events hold the same note. */\nfunction sameNote(note: NoteEvent, other: NoteEvent | undefined): boolean {\n return (\n other !== undefined &&\n note.pitch === other.pitch &&\n note.startBeat === other.startBeat &&\n note.durationBeat === other.durationBeat &&\n note.velocity === other.velocity &&\n note.articulation === other.articulation\n );\n}\n\n/** Whether two meter changes name the same signature at the same beat. */\nfunction sameMeter(change: MeterMap[number], other: MeterMap[number] | undefined): boolean {\n if (other === undefined || change.startBeat !== other.startBeat) {\n return false;\n }\n const grouping = change.ts.grouping ?? [];\n const theirs = other.ts.grouping ?? [];\n return (\n change.ts.numerator === other.ts.numerator &&\n change.ts.denominator === other.ts.denominator &&\n grouping.length === theirs.length &&\n grouping.every((entry, index) => entry === theirs[index])\n );\n}\n\n/** Whether two scores were read in the same key, one having none included. */\nfunction sameKey(key: KeyData | undefined, other: KeyData | undefined): boolean {\n if (key === undefined || other === undefined) {\n return key === other;\n }\n return Key.fromJSON(key).equals(Key.fromJSON(other));\n}\n","import type {\n ArrangementAnalysis,\n ArrangementOptions,\n ArrangementSession,\n ArrangementTrack,\n Conflict,\n TensionPoint,\n TrackEdit,\n} from '../analyze/arrange/index.js';\nimport {\n analyzeArrangement,\n createArrangementSession,\n tensionCurveFrom,\n} from '../analyze/arrange/index.js';\nimport type { KeyRegion } from '../analyze/keys/index.js';\nimport type { ChordTimeline } from '../analyze/timeline/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { MeterLike, MeterMap } from '../core/meter/index.js';\nimport { resolveMeters, toMeterData } from '../core/meter/index.js';\nimport type { NoteEvent } from '../core/types.js';\nimport {\n assertInteger,\n assertNoteEvent,\n assertPositiveInt,\n assertRange,\n} from '../core/validation/index.js';\nimport type { ChordSegment } from '../theory/chord/index.js';\nimport { NoteSafety } from '../theory/safety/index.js';\nimport type { KeyLike } from '../theory/scale/index.js';\nimport { toKeyScale } from '../theory/scale/index.js';\nimport type { ScoreOptions } from './score.js';\nimport { Score } from './score.js';\nimport { copyNoteEvent, samePlain, spanEnd } from './shared.js';\nimport { Timeline } from './timeline.js';\n\n/**\n * The options an arrangement is read under, in the plain form\n * {@link Arrangement.data} carries them.\n *\n * Everything {@link analyzeArrangement} accepts, except that the harmony a\n * caller supplies is held as chord segments: a {@link ChordTimeline} carries a\n * lookup function, and no serialization can hold one.\n */\nexport type ArrangementSettings = Omit<ArrangementOptions, 'timeline'> & {\n /**\n * Chord segments to analyse against, instead of inferring the harmony from\n * the notes.\n */\n timeline?: ChordSegment[];\n};\n\n/**\n * How an arrangement is built: the settings it is read under, with the key\n * named any way the library names one and the harmony given as the timeline\n * the analysis functions take.\n */\nexport type ArrangementSetup = Omit<ArrangementSettings, 'key' | 'meters' | 'timeline'> & {\n /** The key the tracks are read in, when it is already known. */\n key?: KeyLike;\n /**\n * The meter the tracks are read against, as one time signature — `'6/8'` or\n * its data — or as the map of a piece that changes meter. Naming it here and\n * as `ts` too is an input error, since the two name the same thing.\n */\n meters?: MeterLike;\n /**\n * A chord timeline to analyse against, instead of inferring one from the\n * notes.\n */\n timeline?: ChordTimeline;\n};\n\n/** How an arrangement's tension is sampled: its settings, plus the step. */\nexport type ArrangementTensionOptions = ArrangementSetup & {\n /** Distance between samples in beats. */\n step?: number;\n};\n\n/** The plain form an {@link Arrangement} hands out and is rebuilt from. */\nexport type ArrangementData = {\n /**\n * The tracks, in the order they were given.\n *\n * The order is the caller's and is kept: an annotation and a conflict both\n * report the index a note had in the array it arrived in, so reordering the\n * notes here would make those indices point at other notes.\n */\n tracks: ArrangementTrack[];\n /** The options the tracks are read under; absent when they are all defaults. */\n settings?: ArrangementSettings;\n};\n\n/**\n * A deep copy of a plain value.\n *\n * The tracks, the settings and every part of the analysis are plain data —\n * records, arrays and numbers — so one recursive copy keeps a caller from\n * reaching into the arrangement's own state without a bespoke clone of each of\n * the shapes the analysis reports.\n */\nfunction copyPlain<T>(value: T): T {\n if (Array.isArray(value)) {\n return value.map((item: unknown) => copyPlain(item)) as unknown as T;\n }\n if (typeof value === 'object' && value !== null) {\n const copy: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n copy[key] = copyPlain(item);\n }\n return copy as unknown as T;\n }\n return value;\n}\n\n/** Defensive copy of one note event, checked as it is copied. */\nfunction copyNote(note: NoteEvent, name: string): NoteEvent {\n assertNoteEvent(note, name, { allowNonPositiveDuration: true });\n return copyNoteEvent(note);\n}\n\n/**\n * A track's notes checked and copied, in the order they were given.\n *\n * Zero-length notes are accepted, as they are everywhere on the analysis side:\n * a MIDI import carries them, and the analysis drops them itself.\n */\nfunction copyNotes(notes: readonly NoteEvent[], name: string): NoteEvent[] {\n if (!Array.isArray(notes)) {\n throw new InvalidInputError(`${name} must be an array; received ${typeof notes}`);\n }\n return notes.map((note, index) => {\n if (note === undefined) {\n throw new InvalidInputError(`${name}[${index}] must be a note event; received undefined`);\n }\n return copyNote(note, `${name}[${index}]`);\n });\n}\n\n/** Defensive copy of one track, its notes checked as they are copied. */\nfunction copyTrack(track: ArrangementTrack, index: number): ArrangementTrack {\n if (track === undefined) {\n throw new InvalidInputError(`tracks[${index}] must be a track; received undefined`);\n }\n const copy: ArrangementTrack = { notes: copyNotes(track.notes, `tracks[${index}].notes`) };\n if (track.name !== undefined) {\n copy.name = track.name;\n }\n if (track.role !== undefined) {\n copy.role = track.role;\n }\n return copy;\n}\n\n/**\n * Key regions validated and copied by the class that owns the shape.\n *\n * A region carries a key, a confidence and a pivot chord, all of which\n * {@link Timeline} already refuses to hold a nonsensical value for; checking\n * them again here would be a second reading of the same rules.\n */\nfunction copyKeyRegions(keys: readonly KeyRegion[]): KeyRegion[] {\n return [...new Timeline({ segments: [], totalBeats: 0, keys: [...keys] }).keys];\n}\n\n/** Chord segments validated and copied by the class that owns the shape. */\nfunction copySegments(segments: readonly ChordSegment[]): ChordSegment[] {\n return [...new Timeline({ segments: [...segments], totalBeats: spanEnd(segments) }).segments];\n}\n\n/** The `{ at, segments }` shape the analysis functions take, over segments. */\nfunction chordTimelineOf(segments: readonly ChordSegment[]): ChordTimeline {\n return new Timeline({ segments: [...segments], totalBeats: spanEnd(segments) }).chordTimeline;\n}\n\n/**\n * Defensive copy of the settings, with every part validated.\n *\n * This is the boundary a project file or a hand-edited JSON crosses, so each\n * numeric option is checked here under the name it carries rather than\n * wherever the analysis first reads it — an option the analysis only consults\n * on one of its paths is checked on all of them.\n *\n * @param settings The settings as they arrived.\n * @param trackCount How many tracks they are read against.\n * @returns The copy, or undefined when nothing was set.\n * @throws If an option carries a value the analysis cannot hold.\n */\nfunction copySettings(\n settings: ArrangementSettings | undefined,\n trackCount: number,\n): ArrangementSettings | undefined {\n if (settings === undefined) {\n return undefined;\n }\n const copy: ArrangementSettings = {};\n // The meter is read through the same resolver every meter-aware entry point\n // reads its options through, so an arrangement accepts exactly what they\n // accept and naming both `ts` and `meters` is the error it always was.\n const meters = resolveMeters({ ts: settings.ts, meters: settings.meters }, 'arrangement meters');\n if (settings.ts !== undefined) {\n copy.ts = copyPlain(settings.ts);\n }\n if (settings.meters !== undefined) {\n copy.meters = copyPlain(meters);\n }\n if (settings.key !== undefined) {\n copy.key = toKeyScale(settings.key);\n }\n if (settings.keys !== undefined) {\n copy.keys = copyKeyRegions(settings.keys);\n }\n if (settings.timeline !== undefined) {\n copy.timeline = copySegments(settings.timeline);\n }\n if (settings.harmonyTracks !== undefined) {\n copy.harmonyTracks = settings.harmonyTracks.map((track, index) =>\n assertInteger(track, `arrangement harmonyTracks[${index}]`, 0, trackCount - 1),\n );\n }\n if (settings.minSeverity !== undefined) {\n copy.minSeverity = assertInteger(\n settings.minSeverity,\n 'arrangement minSeverity',\n NoteSafety.Safe,\n NoteSafety.Dissonant,\n );\n }\n if (settings.pickupBeats !== undefined) {\n copy.pickupBeats = assertRange(\n settings.pickupBeats,\n 0,\n Number.MAX_SAFE_INTEGER,\n 'arrangement pickupBeats',\n );\n }\n if (settings.harmonicRhythm !== undefined) {\n copy.harmonicRhythm = assertRange(\n settings.harmonicRhythm,\n Number.MIN_VALUE,\n Number.MAX_SAFE_INTEGER,\n 'arrangement harmonicRhythm',\n );\n }\n if (settings.profile !== undefined) {\n copy.profile = settings.profile;\n }\n if (settings.budget !== undefined) {\n copy.budget = assertPositiveInt(settings.budget, 'arrangement budget', Number.MAX_SAFE_INTEGER);\n }\n return Object.keys(copy).length === 0 ? undefined : copy;\n}\n\n/** Defensive copy of plain arrangement data, with every part validated. */\nfunction copyArrangement(data: ArrangementData): ArrangementData {\n if (!Array.isArray(data.tracks)) {\n throw new InvalidInputError(\n `arrangement tracks must be an array; received ${typeof data.tracks}`,\n );\n }\n const tracks = data.tracks.map(copyTrack);\n const settings = copySettings(data.settings, tracks.length);\n return settings === undefined ? { tracks } : { tracks, settings };\n}\n\n/** The plain data a set of tracks and settings describe. */\nfunction arrangementData(\n tracks: readonly ArrangementTrack[],\n settings: ArrangementSettings | undefined,\n): ArrangementData {\n const data: ArrangementData = { tracks: [...tracks] };\n if (settings !== undefined) {\n data.settings = settings;\n }\n return data;\n}\n\n/** The meter map an {@link ArrangementSetup.meters} value names. */\nfunction metersFrom(meters: MeterLike): MeterMap {\n const meter = toMeterData(meters, 'arrangement meters');\n return Array.isArray(meter)\n ? resolveMeters({ meters: meter }, 'arrangement meters')\n : resolveMeters({ ts: meter }, 'arrangement meters');\n}\n\n/** The settings a caller's setup describes, in the plain form data carries. */\nfunction settingsFrom(setup: ArrangementSetup | undefined): ArrangementSettings | undefined {\n if (setup === undefined) {\n return undefined;\n }\n const { key, meters, timeline, ...rest } = setup;\n const settings: ArrangementSettings = { ...rest };\n if (key !== undefined) {\n settings.key = toKeyScale(key);\n }\n if (meters !== undefined) {\n settings.meters = metersFrom(meters);\n }\n if (timeline !== undefined) {\n settings.timeline = [...timeline.segments];\n }\n return settings;\n}\n\n/** A caller's setup as the analysis functions take it: the key and the meter differ. */\nfunction analysisOptionsOf(\n setup: ArrangementTensionOptions | undefined,\n): ArrangementOptions & { step?: number } {\n if (setup === undefined) {\n return {};\n }\n const { key, meters, ...rest } = setup;\n const opts: ArrangementOptions & { step?: number } = { ...rest };\n if (key !== undefined) {\n opts.key = toKeyScale(key);\n }\n if (meters !== undefined) {\n opts.meters = metersFrom(meters);\n }\n return opts;\n}\n\n/**\n * Several named tracks read together, with the analysis of how they fit.\n *\n * One harmony is inferred from every pitched track pooled together, each track\n * is annotated against it, and the notes that clash with the chord sounding\n * beneath them are collected as conflicts — the whole of\n * {@link analyzeArrangement}, held as a value instead of as a call whose\n * result a caller has to carry alongside the notes it was made from.\n *\n * The analysis is held open across edits: {@link Arrangement.update} works out\n * which beats an edit could have reached and recomputes those, so a host that\n * re-analyses on every keystroke pays for the beats that changed. The\n * arrangement it returns is a new one — an arrangement never changes, so the\n * reading taken before an edit stays valid and a host can keep it for undo.\n *\n * A member that has another class to answer with answers with it:\n * {@link Arrangement.track} hands back a {@link Score} and\n * {@link Arrangement.timeline} a {@link Timeline}, so a caller stays in the\n * class API rather than being handed the arrays behind it.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Arrangement } from '@libraz/libcantus';\n * const arrangement = Arrangement.of([\n * { name: 'melody', role: 'melody', notes: [{ pitch: 72, startBeat: 0, durationBeat: 4 }] },\n * { name: 'bass', role: 'bass', notes: [{ pitch: 48, startBeat: 0, durationBeat: 4 }] },\n * ]);\n * arrangement.track('melody')?.totalBeats; // 4\n * ```\n */\nexport class Arrangement {\n readonly #data: ArrangementData;\n /**\n * The open analysis, made on first read and then kept.\n *\n * Deferred rather than made in the constructor: rebuilding an arrangement\n * from stored data, or reading the tracks back out of one, asks nothing of\n * the analysis, and an update hands its own analysis to the arrangement it\n * returns rather than having it made twice.\n */\n #open: ArrangementSession | undefined;\n\n /**\n * Wrap plain arrangement data.\n *\n * @param data The tracks and the settings they are read under; copied, never\n * retained.\n * @throws If a note, the meter, or one of the settings carries a value the\n * analysis cannot hold.\n */\n constructor(data: ArrangementData) {\n this.#data = copyArrangement(data);\n }\n\n /**\n * Build an arrangement from tracks.\n *\n * A track carrying no name is named for its position — `'track 1'`,\n * `'track 2'` — which is the name the analysis reports for it, and the name\n * {@link Arrangement.track} finds it under. A track carrying no role plays\n * `'other'`; only `'drums'` changes the analysis, whose pitches name\n * instruments rather than harmony and are kept out of the inferred key and\n * chords.\n *\n * @param tracks The tracks, in the order the caller holds them.\n * @param opts The key, meter, harmony and budget to read them under; see\n * {@link ArrangementSetup}.\n * @returns The arrangement.\n * @example\n * ```ts\n * import { Arrangement } from '@libraz/libcantus';\n * const arrangement = Arrangement.of(\n * [{ name: 'lead', notes: [{ pitch: 60, startBeat: 0, durationBeat: 4 }] }],\n * { key: 'C major' },\n * );\n * arrangement.tracks.length; // 1\n * ```\n */\n static of(tracks: readonly ArrangementTrack[], opts?: ArrangementSetup): Arrangement {\n return new Arrangement(arrangementData(tracks, settingsFrom(opts)));\n }\n\n /** Rebuild an arrangement from the plain data {@link Arrangement.data} hands out. */\n static fromData(data: ArrangementData): Arrangement {\n return new Arrangement(data);\n }\n\n /** Rebuild an arrangement from its {@link Arrangement.toJSON} output. */\n static fromJSON(data: ArrangementData): Arrangement {\n return new Arrangement(data);\n }\n\n /** The tracks as they were given, in that order. */\n get tracks(): readonly ArrangementTrack[] {\n return copyPlain(this.#data.tracks);\n }\n\n /**\n * The reading of the whole arrangement: the key regions, the harmony, the\n * cadences, each track's annotated notes, and the conflicts.\n *\n * @returns The analysis {@link analyzeArrangement} makes of these tracks.\n */\n get analysis(): ArrangementAnalysis {\n const { timeline, ...rest } = this.#session().analysis;\n return { ...copyPlain(rest), timeline: this.timeline().chordTimeline };\n }\n\n /**\n * The notes clashing with the harmony sounding beneath them, worst severity\n * first.\n *\n * Every ordinary non-chord tone — a passing note, a neighbour, a prepared\n * suspension — is unsafe against the chord under it by definition, so this\n * is a report rather than a fault list; each conflict carries the note's\n * labels to tell those apart.\n */\n get conflicts(): Conflict[] {\n return copyPlain(this.#session().analysis.conflicts);\n }\n\n /**\n * One track's notes as a score, or undefined when no track carries the name.\n *\n * The score is read against the arrangement's meter, and against its key\n * when the arrangement was given one; the harmony the tracks spell out\n * together is what {@link Arrangement.analysis} and\n * {@link Arrangement.timeline} report, since a track read on its own is not\n * read against what sounds beneath it. A score holds its notes in time\n * order, which is not necessarily the order this track's notes arrived in.\n *\n * @param name The track's name, as the analysis reports it.\n * @returns The score, or undefined.\n */\n track(name: string): Score | undefined {\n const index = this.#session().analysis.tracks.findIndex((track) => track.name === name);\n const track = index < 0 ? undefined : this.#data.tracks[index];\n return track === undefined ? undefined : Score.of(track.notes, this.#scoreOptions());\n }\n\n /**\n * Re-analyse after replacing the notes of one or more tracks.\n *\n * The analysis is not made again from nothing: the open reading works out\n * which beats the edit could have reached, recomputes those, and carries the\n * rest over, and the arrangement returned takes that analysis on. What it\n * reports is what a fresh {@link Arrangement.of} over the edited tracks\n * reports — the same segments, keys, cadences, annotations and conflicts;\n * only the work differs.\n *\n * @param edits The tracks whose notes changed, each with the notes it holds\n * after the edit.\n * @returns A new arrangement over the edited tracks; this one is unchanged.\n * @throws If an edit names no track of this arrangement, or carries a note\n * the analysis cannot read.\n * @example\n * ```ts\n * import { Arrangement } from '@libraz/libcantus';\n * const arrangement = Arrangement.of([\n * { name: 'lead', notes: [{ pitch: 60, startBeat: 0, durationBeat: 4 }] },\n * ]);\n * const moved = arrangement.update([\n * { trackIndex: 0, notes: [{ pitch: 62, startBeat: 0, durationBeat: 4 }] },\n * ]);\n * moved.tracks[0]?.notes[0]?.pitch; // 62\n * arrangement.tracks[0]?.notes[0]?.pitch; // 60\n * ```\n */\n update(edits: readonly TrackEdit[]): Arrangement {\n // The reading keeps the arrays it is handed, so it is handed copies: an\n // arrangement must not be reachable through an array a caller still holds.\n const copied: TrackEdit[] = [];\n for (let index = 0; index < edits.length; index += 1) {\n const edit = edits[index];\n if (edit === undefined) {\n continue;\n }\n copied.push({\n trackIndex: edit.trackIndex,\n notes: copyNotes(edit.notes, `edits[${index}].notes`),\n });\n }\n const session = this.#session().update(copied);\n const next = new Arrangement(arrangementData(session.tracks, this.#data.settings));\n next.#adopt(session);\n return next;\n }\n\n /**\n * The harmonic tension across the arrangement, sampled at regular beats.\n *\n * The harmony is the one the arrangement already found, so the curve and the\n * annotations describe the same chords rather than two readings of the same\n * notes.\n *\n * @param opts The sampling step, and any setting to lay over the\n * arrangement's own; a meter named here replaces the arrangement's, since\n * `ts` and `meters` name the same thing.\n * @returns One tension reading per sampled beat, in beat order.\n */\n tension(opts?: ArrangementTensionOptions): TensionPoint[] {\n return tensionCurveFrom(\n [...this.#data.tracks],\n this.#session().analysis,\n this.#layered(analysisOptionsOf(opts)),\n );\n }\n\n /**\n * The whole reading of the arrangement in one call, made afresh.\n *\n * {@link Arrangement.analysis} answers the same question about the\n * arrangement as it stands, and is the member to reach for: it is made once\n * and kept across edits. This one runs {@link analyzeArrangement} again over\n * the same tracks under settings laid on top of the arrangement's own, which\n * is what a caller narrowing the conflict list, restricting the harmony to a\n * few tracks, or reading the piece against a key it does not carry wants —\n * without building a second arrangement to hold that reading.\n *\n * @param opts Any setting to lay over the arrangement's own; a meter named\n * here replaces the arrangement's, since `ts` and `meters` name the same\n * thing.\n * @returns The inferred harmony, per-track annotations, cadences, and\n * conflicts.\n * @throws If a setting carries a value the analysis cannot hold, or the\n * tracks exceed the budget.\n */\n analyze(opts?: ArrangementSetup): ArrangementAnalysis {\n return analyzeArrangement([...this.#data.tracks], this.#layered(analysisOptionsOf(opts)));\n }\n\n /**\n * The harmony the tracks spell out together, in time.\n *\n * @returns The inferred timeline, carrying the key regions the analysis\n * found under it.\n */\n timeline(): Timeline {\n const { timeline, keys } = this.#session().analysis;\n return Timeline.fromData({\n segments: [...timeline.segments],\n // The analysis reports where the chords and the keys end rather than the\n // span it ran over, so the span is read back off them.\n totalBeats: spanEnd(timeline.segments, keys),\n keys: [...keys],\n });\n }\n\n /**\n * Whether another arrangement holds the same tracks under the same settings.\n *\n * The comparison is made through the other arrangement's public data, so two\n * arrangements built by different copies of the module still compare. The\n * analysis is not compared: it is what these tracks and these settings\n * produce, so two arrangements agreeing on both agree on it.\n *\n * @param other The arrangement to compare.\n * @returns True when the tracks and the settings match.\n */\n equals(other: Arrangement): boolean {\n return samePlain(this.#data, other.data);\n }\n\n /** A copy of the underlying plain data. */\n get data(): ArrangementData {\n return copyPlain(this.#data);\n }\n\n /** The plain form of the arrangement, for `JSON.stringify`. */\n toJSON(): ArrangementData {\n return this.data;\n }\n\n /** The open reading, analysing the tracks on the first call. */\n #session(): ArrangementSession {\n const open =\n this.#open ?? createArrangementSession([...this.#data.tracks], this.#analysisOptions());\n this.#open = open;\n return open;\n }\n\n /**\n * Take on an analysis an update has already made.\n *\n * Private to the class rather than to the instance, which is what lets an\n * update hand its incremental reading to the arrangement it returns instead\n * of that arrangement making the same reading again.\n */\n #adopt(session: ArrangementSession): void {\n this.#open = session;\n }\n\n /** The stored settings as the options the analysis functions take. */\n #analysisOptions(): ArrangementOptions {\n const settings = this.#data.settings;\n if (settings === undefined) {\n return {};\n }\n const { timeline, ...rest } = settings;\n return timeline === undefined ? { ...rest } : { ...rest, timeline: chordTimelineOf(timeline) };\n }\n\n /**\n * The arrangement's own options with a caller's laid over them.\n *\n * A meter named by the caller replaces the arrangement's rather than joining\n * it: `ts` and `meters` name the same thing, and carrying one of each is the\n * input error the resolver refuses.\n */\n #layered<T extends ArrangementOptions>(theirs: T): T & ArrangementOptions {\n const mine = this.#analysisOptions();\n const { ts, meters, ...withoutMeter } = mine;\n const inherited = theirs.ts === undefined && theirs.meters === undefined ? mine : withoutMeter;\n return { ...inherited, ...theirs };\n }\n\n /** The context one track's notes are read as a score against. */\n #scoreOptions(): ScoreOptions {\n const settings = this.#data.settings;\n const opts: ScoreOptions = {};\n if (settings?.meters !== undefined) {\n opts.meters = settings.meters;\n } else if (settings?.ts !== undefined) {\n opts.meters = settings.ts;\n }\n if (settings?.key !== undefined) {\n opts.key = settings.key;\n }\n return opts;\n }\n}\n","import { InvalidInputError } from '../core/errors/index.js';\nimport type {\n Articulation,\n InstrumentProfile,\n InstrumentProfileLike,\n Limb,\n PercussionProfile,\n PlayabilityReport,\n StringedProfile,\n StringFingering,\n} from '../core/instrument/index.js';\nimport {\n BASS_4_STRING,\n BASS_5_STRING,\n canSound,\n fingeringsFor,\n foldIntoRange,\n GUITAR_DROP_D,\n GUITAR_STANDARD,\n instrumentRange,\n playability,\n toInstrumentProfile,\n} from '../core/instrument/index.js';\nimport type { NoteLike } from '../core/pitch/index.js';\nimport { toNoteData } from '../core/pitch/index.js';\nimport type { NoteEvent } from '../core/types.js';\nimport { assertInteger, assertPositiveInt } from '../core/validation/index.js';\nimport type { TransposingInstrument } from '../theory/transposition/index.js';\nimport { toSoundingPitch } from '../theory/transposition/index.js';\nimport { Note } from './note.js';\n\n/** Whether two lists hold the same values in the same order. */\nfunction sameList<T>(a: readonly T[], b: readonly T[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n\n/** Whether two kits place the same limbs on the same voices. */\nfunction sameReach(a: PercussionProfile['reach'], b: PercussionProfile['reach']): boolean {\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((key) => sameList(a[Number(key)] ?? [], b[Number(key)] ?? []))\n );\n}\n\n/**\n * A copy of a profile, lists and all, so a caller's object cannot become\n * library state and a profile handed out cannot be edited from underneath the\n * instrument that holds it.\n */\nfunction copyProfile(profile: InstrumentProfile): InstrumentProfile {\n const common = {\n name: profile.name,\n articulations: [...profile.articulations],\n polyphony: profile.polyphony,\n };\n if (profile.kind === 'stringed') {\n const stringed: StringedProfile = {\n kind: 'stringed',\n ...common,\n tuning: [...profile.tuning],\n frets: profile.frets,\n maxStretch: profile.maxStretch,\n };\n // Carried only when the neck has none, so a profile that was copied and one\n // that was written by hand serialize to the same object.\n if (profile.fretless === true) {\n stringed.fretless = true;\n }\n return stringed;\n }\n const reach: Record<number, readonly Limb[]> = {};\n for (const [pitch, limbs] of Object.entries(profile.reach)) {\n reach[Number(pitch)] = [...limbs];\n }\n return { kind: 'percussion', ...common, limbs: [...profile.limbs], reach };\n}\n\n/**\n * A copy of a profile whose numbers the instrument can hold.\n *\n * A fret count that is not a whole number, or a string tuned to no pitch,\n * describes an instrument that answers every question and means none of them:\n * the range it reports, the positions it offers and the passages it accepts are\n * all read off these numbers.\n */\nfunction checkedProfile(profile: InstrumentProfileLike): InstrumentProfile {\n const copy = copyProfile(toInstrumentProfile(profile));\n assertPositiveInt(copy.polyphony, `${copy.name} polyphony`);\n if (copy.kind === 'stringed') {\n for (const [index, open] of copy.tuning.entries()) {\n assertInteger(open, `${copy.name} tuning[${index}]`);\n }\n assertInteger(copy.maxStretch, `${copy.name} maxStretch`, 0);\n } else {\n for (const pitch of Object.keys(copy.reach)) {\n assertInteger(Number(pitch), `${copy.name} reach pitch`);\n }\n }\n // The instrument module's own reading of the profile settles the rest: a\n // fret count that is no count, a neck with no strings, or a kit no limb\n // reaches is refused here rather than at the first passage checked against it.\n instrumentRange(copy);\n return copy;\n}\n\n/**\n * An immutable instrument: what it physically is — an open-string tuning and a\n * fret count, or the voices each limb reaches — with the range, the positions\n * and the playability checks of the instrument module bound to it. A thin\n * convenience wrapper over a plain {@link InstrumentProfile}.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.guitar().fingerings(64).length; // 6\n * Instrument.bass4().canSound(27); // false\n * ```\n */\nexport class Instrument {\n readonly #profile: InstrumentProfile;\n\n private constructor(profile: InstrumentProfile) {\n this.#profile = profile;\n }\n\n /**\n * Wrap a plain instrument profile, whether a built-in one or a caller's own.\n *\n * An `Instrument` is accepted too, and comes back as an equal one: the\n * factory is the boundary a profile crosses everywhere in the library, so it\n * takes the same {@link InstrumentProfileLike} every other entry point does\n * rather than making a caller unwrap what it already holds.\n *\n * @param profile The plain profile, or an instrument standing for one.\n * @returns The wrapped instrument.\n * @throws If the profile describes no instrument: a neck with no strings, a\n * fret count that is not a whole number, a kit no limb reaches, or a\n * tuning, stretch or voice count that is not finite.\n * @example\n * ```ts\n * import { GUITAR_DROP_D } from '../../src/core/index.js';\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.of(GUITAR_DROP_D).range().low; // 38\n * ```\n */\n static of(profile: InstrumentProfileLike): Instrument {\n return new Instrument(checkedProfile(profile));\n }\n\n /**\n * Six-string guitar in standard tuning: E2 A2 D3 G3 B3 E4.\n *\n * @returns The instrument.\n */\n static guitar(): Instrument {\n return Instrument.of(GUITAR_STANDARD);\n }\n\n /**\n * Six-string guitar in drop D: the lowest string down a tone to D2, the rest\n * standard.\n *\n * @returns The instrument.\n */\n static guitarDropD(): Instrument {\n return Instrument.of(GUITAR_DROP_D);\n }\n\n /**\n * Electric bass in standard tuning: E1 A1 D2 G2.\n *\n * @returns The instrument.\n */\n static bass4(): Instrument {\n return Instrument.of(BASS_4_STRING);\n }\n\n /**\n * Five-string bass with the low B: B0 E1 A1 D2 G2.\n *\n * @returns The instrument.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.bass5().range().low; // 23\n * ```\n */\n static bass5(): Instrument {\n return Instrument.of(BASS_5_STRING);\n }\n\n /**\n * Rebuild an instrument from the plain profile {@link Instrument.data} hands\n * out.\n *\n * @param data The plain profile.\n * @returns The wrapped instrument.\n * @throws If the profile describes no instrument; see {@link Instrument.of}.\n */\n static fromData(data: InstrumentProfile): Instrument {\n return Instrument.of(data);\n }\n\n /**\n * Rebuild an instrument from its {@link Instrument.toJSON} output.\n *\n * @param data The serialized profile.\n * @returns The wrapped instrument.\n * @throws If the profile describes no instrument; see {@link Instrument.of}.\n */\n static fromJSON(data: InstrumentProfile): Instrument {\n return Instrument.of(data);\n }\n\n /** The instrument's name, as its profile carries it. */\n get name(): string {\n return this.#profile.name;\n }\n\n /** Which family the instrument belongs to. */\n get kind(): InstrumentProfile['kind'] {\n return this.#profile.kind;\n }\n\n /** Greatest number of notes that can sound at once. */\n get polyphony(): number {\n return this.#profile.polyphony;\n }\n\n /** A copy of the techniques the instrument can produce. */\n get articulations(): Articulation[] {\n return [...this.#profile.articulations];\n }\n\n /** A copy of the underlying plain profile. */\n get data(): InstrumentProfile {\n return this.toJSON();\n }\n\n /**\n * The lowest and highest pitch the instrument sounds.\n *\n * Derived from the tuning and the fret count, or from the voices the\n * player's limbs reach, rather than stored — and not necessarily gapless, so\n * {@link Instrument.canSound} answers for one pitch.\n *\n * @returns The extreme sounding pitches as MIDI numbers, inclusive.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.guitar().range(); // { low: 40, high: 88 }\n * ```\n */\n range(): { low: number; high: number } {\n return instrumentRange(this.#profile);\n }\n\n /**\n * Whether the instrument has this pitch at all.\n *\n * @param pitch MIDI pitch.\n * @returns True when some string and fret, or some limb the player has,\n * produces it.\n */\n canSound(pitch: number): boolean {\n return canSound(this.#profile, pitch);\n }\n\n /**\n * Every position on the neck that sounds a pitch, lowest string first.\n *\n * @param pitch MIDI pitch.\n * @returns One entry per string that reaches the pitch; empty when none does.\n * @throws If the instrument is a kit, which has no neck to place a pitch on.\n */\n fingerings(pitch: number): StringFingering[] {\n const profile = this.#profile;\n if (profile.kind !== 'stringed') {\n throw new InvalidInputError(`${profile.name} has no strings to finger`);\n }\n return fingeringsFor(profile, pitch);\n }\n\n /**\n * Move a pitch by whole octaves until the instrument can sound it, which is\n * what a player does with a line written below the instrument.\n *\n * @param pitch MIDI pitch to place.\n * @returns The nearest octave transposition the instrument sounds, searching\n * upward first; the pitch unchanged when no transposition is available.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.bass4().foldIntoRange(27); // 39\n * ```\n */\n foldIntoRange(pitch: number): number {\n return foldIntoRange(pitch, this.#profile);\n }\n\n /**\n * The pitch this instrument sounds for a note written in its part.\n *\n * Most parts are written at the pitch they sound, but some are not: a guitar\n * part is printed an octave above concert pitch, and a part for an instrument\n * in B flat or in A is printed in a different key altogether. The instrument\n * is looked up by its own name, so an instrument the transposition table\n * carries needs nothing said; name a transposition to read a part for one it\n * does not, which is every instrument whose profile is the caller's own.\n *\n * The interval decides the letter, so the spelling of the part survives: on a\n * clarinet in A a written D sharp sounds B sharp, where a semitone count\n * alone would answer C natural and lose the letter the part is written on.\n *\n * @param note A note name, a MIDI number, plain note data, or a `Note`, as\n * the player reads it.\n * @param transposition The written-to-sounding transposition to read the part\n * under; the instrument's own name by default.\n * @returns The sounding note, at concert pitch.\n * @throws If the note is malformed, or the transposition is neither a known\n * instrument name nor a spelled interval — which is what an instrument the\n * table does not carry reports when none is named.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.guitar().soundingPitch('C4').name; // 'C3'\n * Instrument.bass4().soundingPitch('C4', '-P8').name; // 'C3'\n * ```\n */\n soundingPitch(note: NoteLike, transposition: TransposingInstrument = this.#profile.name): Note {\n return new Note(toSoundingPitch(toNoteData(note), transposition));\n }\n\n /**\n * Judge whether a passage can be played on this instrument, and how hard it\n * is.\n *\n * Nothing is rewritten or rejected: the report names what stands in the way,\n * and only the tempo-dependent layer consults `bpm`, so omitting the tempo\n * reports what the instrument alone decides.\n *\n * @param notes The passage, in any order. Drum hits qualify as note events.\n * @param bpm Tempo in quarter-note beats per minute; enables the layer that\n * asks whether there is time for the movement.\n * @returns Difficulty, the issues found, and each note's string/fret or limb.\n * @throws If the notes are not note events, or the tempo is not a finite\n * positive number of beats per minute.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * const report = Instrument.bass4().playability([\n * { pitch: 27, startBeat: 0, durationBeat: 1 },\n * ]);\n * report.issues[0]?.type; // 'noteOutOfRange'\n * ```\n */\n playability(notes: readonly NoteEvent[], bpm?: number): PlayabilityReport {\n return playability(notes, this.#profile, bpm);\n }\n\n /**\n * Whether another instrument is described identically.\n *\n * @param other The instrument to compare with.\n * @returns True when both profiles name the same instrument, string for\n * string or voice for voice.\n */\n equals(other: Instrument): boolean {\n // The other instrument is read through its public surface rather than its\n // private field: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n const a = this.#profile;\n const b = other.data;\n if (a.name !== b.name || a.kind !== b.kind || a.polyphony !== b.polyphony) {\n return false;\n }\n if (!sameList(a.articulations, b.articulations)) {\n return false;\n }\n if (a.kind === 'stringed') {\n return (\n b.kind === 'stringed' &&\n a.frets === b.frets &&\n a.maxStretch === b.maxStretch &&\n (a.fretless ?? false) === (b.fretless ?? false) &&\n sameList(a.tuning, b.tuning)\n );\n }\n return b.kind === 'percussion' && sameList(a.limbs, b.limbs) && sameReach(a.reach, b.reach);\n }\n\n /**\n * The plain profile, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(instrument)` from collapsing to `{}`.\n *\n * @returns A copy of the underlying profile, its lists included.\n */\n toJSON(): InstrumentProfile {\n return copyProfile(this.#profile);\n }\n\n /**\n * The instrument's name, so a template literal or a log line reads as the\n * instrument.\n *\n * @returns The name, e.g. `'guitar'`.\n * @example\n * ```ts\n * import { Instrument } from '@libraz/libcantus';\n * Instrument.guitar().toString(); // 'guitar'\n * ```\n */\n toString(): string {\n return this.#profile.name;\n }\n}\n","import type { ChordTimeline } from '../analyze/timeline/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { InstrumentProfile, InstrumentProfileLike } from '../core/instrument/profile.js';\nimport { toStringedProfile } from '../core/instrument/profile.js';\nimport type { MeterLike, MeterMap, TimeSignature } from '../core/meter/index.js';\nimport { beatsPerBar, meterAt, resolveMeters, toMeterData } from '../core/meter/index.js';\nimport type { KeyScale } from '../core/types.js';\nimport { assertFiniteNumber } from '../core/validation/index.js';\nimport type { BassLineOptions, BassSegment } from '../generate/bass/index.js';\nimport { generateBassLine } from '../generate/bass/index.js';\nimport type { Complexity, GenerationContext } from '../generate/context/index.js';\nimport { resolveContext } from '../generate/context/index.js';\nimport type { CounterMelodyOptions } from '../generate/countermelody/index.js';\nimport { generateCounterMelody } from '../generate/countermelody/index.js';\nimport type { DrumsOptions } from '../generate/drums/index.js';\nimport { generateDrums } from '../generate/drums/index.js';\nimport type { HarmonizeOptions } from '../generate/harmonize/index.js';\nimport { harmonizeMelody } from '../generate/harmonize/index.js';\nimport type { ProgressionOptions } from '../generate/progression/index.js';\nimport { generateProgression } from '../generate/progression/index.js';\nimport type { Vocabulary } from '../generate/vocabulary/types.js';\nimport type { ChordSpan } from '../theory/chord/index.js';\nimport type { KeyLike } from '../theory/scale/index.js';\nimport { majorKey, toKeyScale } from '../theory/scale/index.js';\nimport { Instrument } from './instrument.js';\nimport type { ScoreOptions } from './score.js';\nimport { Score } from './score.js';\nimport { samePlain } from './shared.js';\nimport { Timeline } from './timeline.js';\n\n/** The settings a composer holds and hands to every generator it drives. */\nexport type ComposerOptions = {\n /** The key the parts are written in. */\n key?: KeyLike;\n /** The meter, as one time signature — `'6/8'` or its data — or a map of changes. */\n meters?: MeterLike;\n /** Tempo in quarter-note beats per minute. */\n bpm?: number;\n /** The seed every part's own stream is derived from. */\n seed?: number;\n /** The complexity dials. */\n complexity?: Complexity;\n /**\n * The instrument each named part is written for, as a plain\n * {@link InstrumentProfile} or an {@link Instrument}. The settings a composer\n * hands back carry the plain form, the way a key handed in as a name comes\n * back as key/scale data.\n */\n instruments?: Record<string, InstrumentProfileLike>;\n /** Extra material the generators may draw from. */\n vocabulary?: readonly Vocabulary<unknown>[];\n};\n\n/**\n * How a bass line is written: what {@link BassLineOptions} asks for minus the\n * settings the composer already holds, with the instrument taken the way the\n * rest of the class API takes one.\n */\nexport type BassLineSettings = Omit<\n BassLineOptions,\n 'segments' | 'key' | 'ts' | 'ctx' | 'instrument'\n> & {\n /**\n * The instrument the line is written for, as a plain {@link StringedProfile}\n * or an {@link Instrument}. Giving one is itself the request that the line be\n * playable on it: `octave` then says where in that instrument to aim rather\n * than which absolute band to use, and where the two disagree the instrument\n * wins — a note below the lowest string comes back an octave up, the way a\n * player would take it. Leave it out for a programmed part.\n */\n instrument?: InstrumentProfileLike;\n};\n\n/**\n * How a counter line is written: what {@link CounterMelodyOptions} asks for\n * minus the settings the composer already holds, with the harmony taken the way\n * {@link Composer.bass} takes it.\n */\nexport type CounterMelodySettings = Omit<\n CounterMelodyOptions,\n 'melody' | 'key' | 'ts' | 'ctx' | 'timeline'\n> & {\n /**\n * The harmony to write against, as a {@link Timeline} or the plain chord\n * timeline the analysis layer hands out. A timeline carries its own segment\n * boundaries, so a chord change anywhere — including off the generator's\n * half-beat probe grid — is seen; prefer it over `chordAt`.\n */\n timeline?: Timeline | ChordTimeline;\n};\n\n/** What a harmonization gives back: the chords, and the melody they fit. */\nexport type HarmonizedMelody = {\n /** The chords, in time. */\n chords: Timeline;\n /**\n * The melody as the chords read it. Harmonizing may move the melody into the\n * key the chords are in, so this is the line that actually sounds against\n * them; the untransposed input would not.\n */\n melody: Score;\n /** How far the melody was moved, in semitones. */\n transposeSemitones: number;\n};\n\n/**\n * Beats of one progression bar.\n *\n * {@link generateProgression} lays out one chord per four-beat bar whatever\n * meter the rest of the piece is in, so the span its chords cover is counted\n * against that bar and not against the composer's own.\n */\nconst PROGRESSION_BAR_BEATS = 4;\n\n/** The key a composer that names none writes in. */\nconst DEFAULT_KEY: KeyScale = majorKey(0);\n\n/**\n * A deep copy of caller data, with every number checked and every absent field\n * left out.\n *\n * The vocabulary a composer carries holds a `material` of whatever shape the\n * figure is made of, and an instrument profile is a plain record too, so the\n * only way to promise that nothing non-finite reaches a generator — or survives\n * a round trip through a project file — is to walk the value. Dropping\n * `undefined` properties is what keeps the copy equal to its own JSON.\n */\nfunction copyPlain<T>(value: T, name: string): T {\n if (typeof value === 'number') {\n return assertFiniteNumber(value, name) as T;\n }\n if (Array.isArray(value)) {\n return value.map((item, index) => copyPlain(item, `${name}[${index}]`)) as T;\n }\n if (typeof value === 'object' && value !== null) {\n const copy: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n if (item !== undefined) {\n copy[key] = copyPlain(item, `${name}.${key}`);\n }\n }\n return copy as T;\n }\n if (typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint') {\n throw new InvalidInputError(`${name} must be plain data; received ${typeof value}`);\n }\n return value;\n}\n\n/**\n * The plain chord timeline a harmony value stands for.\n *\n * Read through the public surface rather than by `instanceof`, so a timeline\n * built by a second copy of the module is followed like any other — the same\n * reading {@link Composer.bass} and {@link Motif.develop} take.\n */\nfunction toChordTimeline(harmony: Timeline | ChordTimeline): ChordTimeline {\n return 'chordTimeline' in harmony ? harmony.chordTimeline : harmony;\n}\n\n/** The meter map a {@link ComposerOptions.meters} value names. */\nfunction metersFrom(meters: MeterLike | undefined): MeterMap {\n if (meters === undefined) {\n return resolveMeters({}, 'composer meters');\n }\n const meter = toMeterData(meters, 'composer meters');\n return Array.isArray(meter)\n ? resolveMeters({ meters: meter }, 'composer meters')\n : resolveMeters({ ts: meter }, 'composer meters');\n}\n\n/** A copy of the dials, carrying only the ones the caller named. */\nfunction copyComplexity(complexity: Complexity): Complexity {\n const copy: Complexity = {};\n for (const dial of ['rhythmic', 'harmonic', 'ornament', 'difficulty'] as const) {\n const value = complexity[dial];\n if (value !== undefined) {\n copy[dial] = assertFiniteNumber(value, `composer complexity ${dial}`);\n }\n }\n return copy;\n}\n\n/** A copy of the instrument each part is written for, each one validated. */\nfunction copyInstruments(\n instruments: Record<string, InstrumentProfileLike>,\n): Record<string, InstrumentProfile> {\n const copy: Record<string, InstrumentProfile> = {};\n for (const [part, profile] of Object.entries(instruments)) {\n // Checked and copied by the class that speaks for a profile everywhere\n // else, so a composer accepts exactly the instruments an instrument does.\n copy[part] = Instrument.of(profile).data;\n }\n return copy;\n}\n\n/**\n * A deep copy of the settings in the canonical plain form.\n *\n * A key becomes the plain key/scale every generator takes, and a bare time\n * signature the meter map the whole model layer reads, so the settings a\n * composer hands out are the settings it hands on. The meter is materialized\n * because a composer that names none is read in 4/4 exactly as one that names\n * it is; the key is not, because a composer with no key harmonizes by inferring\n * one, which is a different request from harmonizing in C major.\n */\nfunction copyOptions(options: ComposerOptions): ComposerOptions {\n const copy: ComposerOptions = { meters: metersFrom(options.meters) };\n if (options.key !== undefined) {\n copy.key = toKeyScale(options.key);\n }\n if (options.bpm !== undefined) {\n copy.bpm = options.bpm;\n }\n if (options.seed !== undefined) {\n copy.seed = options.seed;\n }\n if (options.complexity !== undefined) {\n copy.complexity = copyComplexity(options.complexity);\n }\n if (options.instruments !== undefined) {\n copy.instruments = copyInstruments(options.instruments);\n }\n if (options.vocabulary !== undefined) {\n copy.vocabulary = options.vocabulary.map((entry, index) =>\n copyPlain(entry, `composer vocabulary[${index}]`),\n );\n }\n return copy;\n}\n\n/** The context half of a set of settings, as the generators read it. */\nfunction contextOf(options: ComposerOptions): GenerationContext {\n const ctx: GenerationContext = {};\n if (options.seed !== undefined) {\n ctx.seed = options.seed;\n }\n if (options.bpm !== undefined) {\n ctx.bpm = options.bpm;\n }\n if (options.complexity !== undefined) {\n ctx.complexity = copyComplexity(options.complexity);\n }\n if (options.instruments !== undefined) {\n ctx.instruments = copyInstruments(options.instruments);\n }\n if (options.vocabulary !== undefined) {\n ctx.vocabulary = options.vocabulary.map((entry) => copyPlain(entry, 'composer vocabulary'));\n }\n return ctx;\n}\n\n/**\n * Where a harmonized span ends.\n *\n * The melody's own end, and never before the last chord: the chords are placed\n * on a grid laid over the melody, and a timeline built to a span the last chord\n * starts on would drop that chord instead of sounding it. The last slot is as\n * long as the one before it, and a bar where there is only one.\n */\nfunction harmonizedSpan(melodyEnd: number, chords: readonly ChordSpan[], barBeats: number): number {\n const last = chords[chords.length - 1];\n if (last === undefined) {\n return melodyEnd;\n }\n const previous = chords[chords.length - 2];\n const slot = previous === undefined ? barBeats : last.startBeat - previous.startBeat;\n return Math.max(melodyEnd, last.startBeat + slot);\n}\n\n/**\n * The settings a piece is generated under, held in one place and handed to\n * every part.\n *\n * The generation context — seed, tempo, complexity dials, instruments,\n * vocabulary — was already designed to be shared across the parts of one\n * piece, and `resolveContext(...).part(name)` already gives each part its own\n * stream from a single seed. What was missing was something to hold the shared\n * half, so callers wrote `key`, `ts`, and `ctx: { seed }` again at every call.\n * A `Composer` is that holder: set it once, and each generator inherits it.\n *\n * Every part a composer writes is scored against the composer's own meter and\n * tempo, and the pitched ones in its key, so the scores it hands back line up\n * with each other without being re-contextualized one at a time.\n *\n * Immutable like every other class here: `withSeed` and friends return a new\n * composer rather than reconfiguring this one, so a variation can be written\n * beside the original instead of replacing it.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Composer } from '@libraz/libcantus';\n * const composer = Composer.of({ key: 'C major', bpm: 96, seed: 7 });\n * composer.progression({ style: 'dance', bars: 4 }).totalBeats; // 16\n * composer.withSeed(8).data.seed; // 8\n * ```\n */\nexport class Composer {\n readonly #options: ComposerOptions;\n readonly #meters: MeterMap;\n readonly #key: KeyScale | undefined;\n\n /**\n * Wrap the settings a piece is generated under.\n *\n * @param options The shared key, meter, tempo, and context; copied, never\n * retained.\n * @throws If the key names no key, the meter map is malformed, or the seed,\n * tempo, dials, instruments or vocabulary carry a value the generators\n * cannot hold.\n */\n constructor(options: ComposerOptions) {\n this.#options = copyOptions(options);\n // The context half is checked by the resolver every generator reads it\n // through, so a composer accepts exactly the seeds, tempos, dials and\n // dictionaries they accept — at the point the settings are named rather\n // than at the first part written under them.\n resolveContext(contextOf(this.#options));\n this.#meters = metersFrom(this.#options.meters);\n this.#key = this.#options.key === undefined ? undefined : toKeyScale(this.#options.key);\n }\n\n /**\n * Start from a set of settings.\n *\n * @param options The shared key, meter, tempo, and context.\n * @returns The composer.\n */\n static of(options: ComposerOptions): Composer {\n return new Composer(options);\n }\n\n /** Rebuild a composer from the plain data {@link Composer.data} hands out. */\n static fromData(data: ComposerOptions): Composer {\n return new Composer(data);\n }\n\n /** Rebuild a composer from its {@link Composer.toJSON} output. */\n static fromJSON(data: ComposerOptions): Composer {\n return new Composer(data);\n }\n\n /** The same settings with some of them replaced. */\n with(patch: Partial<ComposerOptions>): Composer {\n return new Composer({ ...this.#options, ...patch });\n }\n\n /** The same settings in a different key. */\n withKey(key: KeyLike): Composer {\n return this.with({ key });\n }\n\n /** The same settings from a different seed. */\n withSeed(seed: number): Composer {\n return this.with({ seed });\n }\n\n /** The same settings at different complexity. */\n withComplexity(complexity: Complexity): Composer {\n return this.with({ complexity });\n }\n\n /**\n * A chord progression, in time.\n *\n * The chords are laid out one per four-beat bar, which is the grid\n * {@link generateProgression} writes on whatever meter the composer holds, so\n * the timeline spans `bars` of four beats and carries the composer's key.\n *\n * @param opts Everything the progression generator takes but the key and the\n * context, which are the composer's.\n * @returns The chords over the beats they sound for.\n */\n progression(opts: Omit<ProgressionOptions, 'key' | 'ctx'>): Timeline {\n const key = this.#keyScale();\n const chords = generateProgression({ ...opts, key, ctx: this.context });\n return Timeline.fromChords(chords, opts.bars * PROGRESSION_BAR_BEATS, key);\n }\n\n /**\n * A drum part.\n *\n * A drum onset is a note event carrying a General MIDI pitch, so the hits are\n * a score like any other and read against the composer's meter and tempo. It\n * carries no key: a kit is not in one, and naming a key here would invite the\n * pitched analyses to read the kit map as melody.\n *\n * @param opts Everything the drum generator takes but the meter and the\n * context, which are the composer's.\n * @returns The hits, as a score.\n * @throws If the composer's meter is not 4/4, which is the only meter the\n * drum patterns are written against.\n */\n drums(opts: Omit<DrumsOptions, 'ts' | 'ctx'>): Score {\n const hits = generateDrums({ ...opts, ts: this.#openingMeter(), ctx: this.context });\n return Score.of(hits, this.#scoreOptions());\n }\n\n /**\n * A bass line under a harmony.\n *\n * @param source The harmony to follow: a timeline, or the chord segments one\n * is made of.\n * @param opts Everything the bass generator takes but the segments, the key,\n * the meter and the context; see {@link BassLineSettings}.\n * @returns The line, as a score in the composer's key.\n * @throws If the instrument names a kit, which has no strings for a bass line\n * to be placed on.\n * @example\n * ```ts\n * import { Composer, Instrument } from '@libraz/libcantus';\n * const composer = Composer.of({ key: 'C major', seed: 3 });\n * const plan = composer.progression({ style: 'dance', bars: 2 });\n * composer.bass(plan, { style: 'pop', instrument: Instrument.bass4() }).notes.length > 0; // true\n * ```\n */\n bass(source: Timeline | readonly BassSegment[], opts?: BassLineSettings): Score {\n // Read through the public surface rather than by `instanceof`, so a\n // timeline built by a second copy of the module is followed like any other.\n const segments: readonly BassSegment[] = Array.isArray(source)\n ? (source as readonly BassSegment[])\n : (source as Timeline).segments;\n const key = this.#keyScale();\n const { instrument, ...rest } = opts ?? {};\n const notes = generateBassLine({\n ...rest,\n ...(instrument === undefined\n ? {}\n : { instrument: toStringedProfile(instrument, 'bass instrument') }),\n segments,\n key,\n ts: this.#openingMeter(),\n ctx: this.context,\n });\n return Score.of(notes, this.#scoreOptions(key));\n }\n\n /**\n * A second line against a melody.\n *\n * Only the melody's notes cross over; the meter, the tempo and the key the\n * line is written against are the composer's, so a counter line and the part\n * it answers are read in one context.\n *\n * The harmony crosses over the way {@link Composer.bass} takes it: a\n * {@link Timeline} or the plain chord timeline, so a caller holding the\n * class does not have to unwrap it for one of the two methods.\n *\n * @param melody The lead line to write against.\n * @param opts Everything the counter-melody generator takes but the melody,\n * the key, the meter and the context; see {@link CounterMelodySettings}.\n * @returns The counter line, as a score in the composer's key.\n */\n counterMelody(melody: Score, opts?: CounterMelodySettings): Score {\n const key = this.#keyScale();\n const { timeline, ...rest } = opts ?? {};\n const notes = generateCounterMelody({\n ...rest,\n ...(timeline === undefined ? {} : { timeline: toChordTimeline(timeline) }),\n melody: melody.notes,\n key,\n ts: this.#openingMeter(),\n ctx: this.context,\n });\n return Score.of(notes, this.#scoreOptions(key));\n }\n\n /**\n * Chords under a melody, with the melody the chords read.\n *\n * A composer that names a key harmonizes in it; one that names none has the\n * key estimated from the melody, which is what the harmonizer does when it is\n * asked for chords and given nothing else.\n *\n * @param melody The melody to harmonize.\n * @param opts Everything the harmonizer takes but the melody, the key, the\n * meter and the context.\n * @returns The chords, the melody as they read it, and the distance between\n * that melody and the one handed in.\n * @example\n * ```ts\n * import { Composer } from '@libraz/libcantus';\n * import { Score } from '../../src/model/score.js';\n * const composer = Composer.of({ key: 'C major' });\n * const melody = Score.of([{ pitch: 60, startBeat: 0, durationBeat: 4 }]);\n * composer.harmonize(melody).transposeSemitones; // 0\n * ```\n */\n harmonize(\n melody: Score,\n opts?: Omit<HarmonizeOptions, 'melody' | 'key' | 'ts' | 'ctx'>,\n ): HarmonizedMelody {\n const result = harmonizeMelody({\n ...opts,\n melody: melody.notes,\n ...(this.#key === undefined ? {} : { key: this.#key }),\n ts: this.#openingMeter(),\n ctx: this.context,\n });\n const moved = melody.transpose(result.transposeSemitones);\n const totalBeats = harmonizedSpan(\n moved.totalBeats,\n result.chords,\n beatsPerBar(this.#openingMeter()),\n );\n return {\n chords: Timeline.fromChords(result.chords, totalBeats, result.key),\n // Scored in the key the chords are in rather than the composer's: where\n // the harmonizer moved the line, that is the key it now sounds in.\n melody: Score.of(moved.notes, this.#scoreOptions(result.key)),\n transposeSemitones: result.transposeSemitones,\n };\n }\n\n /**\n * Whether another composer holds the same settings.\n *\n * The comparison is made through the other composer's public data, so two\n * composers built by different copies of the module still compare.\n *\n * @param other The composer to compare.\n * @returns True when both would generate the same parts.\n */\n equals(other: Composer): boolean {\n return samePlain(this.#options, other.data);\n }\n\n /** The resolved context, for handing back to the function API. */\n get context(): GenerationContext {\n return contextOf(this.#options);\n }\n\n /** A copy of the settings this composer holds. */\n get data(): ComposerOptions {\n return copyOptions(this.#options);\n }\n\n /** The plain form of the settings, for `JSON.stringify`. */\n toJSON(): ComposerOptions {\n return this.data;\n }\n\n /** The key the parts are written in; C major where the composer names none. */\n #keyScale(): KeyScale {\n return this.#key ?? DEFAULT_KEY;\n }\n\n /**\n * The signature the parts are written against.\n *\n * Each generator names one time signature rather than a map, so a composer\n * whose meter changes hands over the signature its music opens on — the same\n * reading {@link Score.humanize} and its siblings take.\n */\n #openingMeter(): TimeSignature {\n return meterAt(0, this.#meters);\n }\n\n /** The context every score a composer hands back is read against. */\n #scoreOptions(key?: KeyScale): ScoreOptions {\n const opts: ScoreOptions = { meters: this.#meters };\n if (this.#options.bpm !== undefined) {\n opts.tempo = this.#options.bpm;\n }\n if (key !== undefined) {\n opts.key = key;\n }\n return opts;\n }\n}\n","import type { DurationData, NoteValue, SpelledDuration, Tuplet } from '../core/duration/index.js';\nimport { beatsToDuration, beatsToTiedDurations, durationToBeats } from '../core/duration/index.js';\n\n/**\n * A defensive copy of a written duration, with its dot count stated.\n *\n * The reading is the check: `durationToBeats` runs the duration module's own\n * validation — the base values it knows, the dot ceiling, the tuplet bounds —\n * so a duration arriving from a project file is held to exactly the rules a\n * duration built here is, without this layer restating any of them.\n */\nfunction copyDuration(value: NoteValue | DurationData): SpelledDuration {\n durationToBeats(value);\n if (typeof value === 'string') {\n return { base: value, dots: 0 };\n }\n const spelled: SpelledDuration = { base: value.base, dots: value.dots ?? 0 };\n if (value.tuplet !== undefined) {\n spelled.tuplet = { actual: value.tuplet.actual, normal: value.tuplet.normal };\n }\n return spelled;\n}\n\n/**\n * An immutable written duration: the note value a score shows — a base value,\n * its augmentation dots, and an optional tuplet ratio — and the beats it\n * lasts. A thin convenience wrapper over the duration module's plain value.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Duration } from '@libraz/libcantus';\n * Duration.ofBeats(1.5).spelled; // { base: 'quarter', dots: 1 }\n * ```\n */\nexport class Duration {\n readonly #base: NoteValue;\n readonly #dots: number;\n readonly #tuplet: Tuplet | undefined;\n\n private constructor(value: NoteValue | DurationData) {\n const spelled = copyDuration(value);\n this.#base = spelled.base;\n this.#dots = spelled.dots;\n this.#tuplet = spelled.tuplet;\n }\n\n /**\n * Build a duration from its parts.\n *\n * @param base The base note value, from the whole note down to the\n * sixty-fourth.\n * @param dots Augmentation dots; up to four are accepted.\n * @param tuplet Optional tuplet ratio: `{ actual: 3, normal: 2 }` writes\n * three notes in the time of two.\n * @returns The duration.\n * @throws If the base value is unknown, the dot count is not an integer in\n * 0..4, or a tuplet side is not a positive integer.\n */\n static of(base: NoteValue, dots = 0, tuplet?: Tuplet): Duration {\n return new Duration(tuplet === undefined ? { base, dots } : { base, dots, tuplet });\n }\n\n /**\n * Spell a length in beats as the note value a score would show.\n *\n * The spelling is the conventional one: 1.5 beats is a dotted quarter rather\n * than a quarter tied to an eighth, and a third of a beat is an eighth\n * triplet rather than a twenty-fourth note.\n *\n * @param beats The length in beats.\n * @param options `beatUnit` sets what one beat is; a quarter note by\n * default.\n * @returns The duration that spells the length.\n * @throws If the length is not positive, or — as a `NoSolutionError` — if no\n * single note value spells it, as for 5 beats. Use\n * {@link Duration.tieChain} for those.\n */\n static ofBeats(beats: number, options: { beatUnit?: NoteValue | DurationData } = {}): Duration {\n return new Duration(beatsToDuration(beats, options));\n }\n\n /**\n * Spell a length in beats as a chain of tied durations.\n *\n * A length with a single spelling comes back as a one-element chain.\n * Anything else is broken into plain and dotted values, longest first. The\n * chain knows nothing of barlines or beat grouping — an engraver splits\n * further at those — and it uses no tuplets.\n *\n * @param beats The length in beats.\n * @param options `beatUnit` sets what one beat is; a quarter note by\n * default.\n * @returns The tied durations, longest first, summing to the length.\n * @throws If the length is not positive, if the chain would exceed the\n * generation budget, or — as a `NoSolutionError` — if no chain of plain and\n * dotted values sums to it.\n * @example\n * ```ts\n * import { Duration } from '@libraz/libcantus';\n * Duration.tieChain(5).map((value) => value.toString()); // ['whole', 'quarter']\n * ```\n */\n static tieChain(\n beats: number,\n options: { beatUnit?: NoteValue | DurationData } = {},\n ): Duration[] {\n return beatsToTiedDurations(beats, options).map((spelled) => new Duration(spelled));\n }\n\n /**\n * Wrap a plain duration, or a bare base value as shorthand for an undotted\n * one.\n *\n * @param data The plain duration.\n * @returns The wrapped duration.\n * @throws If the base value is unknown, the dot count is not an integer in\n * 0..4, or a tuplet side is not a positive integer.\n */\n static fromData(data: NoteValue | DurationData): Duration {\n return new Duration(data);\n }\n\n /**\n * Rebuild a duration from its {@link Duration.toJSON} output.\n *\n * @param data The serialized duration.\n * @returns The wrapped duration.\n * @throws If the base value is unknown, the dot count is not an integer in\n * 0..4, or a tuplet side is not a positive integer.\n */\n static fromJSON(data: NoteValue | DurationData): Duration {\n return new Duration(data);\n }\n\n /** The base note value, from the whole note down to the sixty-fourth. */\n get base(): NoteValue {\n return this.#base;\n }\n\n /** Augmentation dots, 0 when the value carries none. */\n get dots(): number {\n return this.#dots;\n }\n\n /** A copy of the tuplet ratio, or undefined outside a tuplet. */\n get tuplet(): Tuplet | undefined {\n return this.#tuplet === undefined\n ? undefined\n : { actual: this.#tuplet.actual, normal: this.#tuplet.normal };\n }\n\n /**\n * A copy of the written value, with the dot count always stated: the shape\n * the duration module spells a length as.\n */\n get spelled(): SpelledDuration {\n return this.toJSON();\n }\n\n /** A copy of the underlying plain duration, the same value as {@link Duration.spelled}. */\n get data(): SpelledDuration {\n return this.toJSON();\n }\n\n /**\n * The length of the written value in beats.\n *\n * @param options `beatUnit` sets what one beat is; a quarter note by\n * default, so a dotted quarter reads as 1.5. Pass\n * `{ base: 'quarter', dots: 1 }` to count in the felt beats of a compound\n * meter.\n * @returns The length in beats.\n * @throws If the beat unit is not a duration the library reads.\n * @example\n * ```ts\n * import { Duration } from '@libraz/libcantus';\n * Duration.of('quarter', 1).beats(); // 1.5\n * ```\n */\n beats(options: { beatUnit?: NoteValue | DurationData } = {}): number {\n return durationToBeats(this.toJSON(), options);\n }\n\n /**\n * Whether another duration is written identically.\n *\n * Two values of the same length are not equal unless they are written the\n * same way: a dotted quarter and a quarter in the time of a triplet half\n * both last 1.5 beats and are different notations.\n *\n * @param other The duration to compare with.\n * @returns True when base, dots, and tuplet all match.\n */\n equals(other: Duration): boolean {\n // The other duration is read through its public surface rather than its\n // private fields: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n const b = other.data;\n return (\n this.#base === b.base &&\n this.#dots === b.dots &&\n this.#tuplet?.actual === b.tuplet?.actual &&\n this.#tuplet?.normal === b.tuplet?.normal\n );\n }\n\n /**\n * The plain duration, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(duration)` from collapsing to `{}`. The tuplet appears\n * only on a value that carries one, which is the shape the duration module\n * produces.\n *\n * @returns A copy of the base value, dot count, and tuplet.\n */\n toJSON(): SpelledDuration {\n return this.#tuplet === undefined\n ? { base: this.#base, dots: this.#dots }\n : {\n base: this.#base,\n dots: this.#dots,\n tuplet: { actual: this.#tuplet.actual, normal: this.#tuplet.normal },\n };\n }\n\n /**\n * The written value, so a template literal or a log line reads as the\n * duration.\n *\n * @returns The base value, a dot per augmentation dot, and the tuplet ratio\n * when it has one, e.g. `'quarter.'` or `'eighth 3:2'`.\n */\n toString(): string {\n const dots = '.'.repeat(this.#dots);\n const ratio =\n this.#tuplet === undefined ? '' : ` ${this.#tuplet.actual}:${this.#tuplet.normal}`;\n return `${this.#base}${dots}${ratio}`;\n }\n}\n","import type { ParseResult } from '../core/errors/index.js';\nimport type { BarPosition, TimeSignature } from '../core/meter/index.js';\nimport {\n beatsPerBar,\n beatToBarPosition,\n formatBarPosition,\n formatTimeSignature,\n isCompound,\n isStrongBeat,\n metricWeight,\n parseTimeSignature,\n pulseBeats,\n pulsesPerBar,\n tryParseTimeSignature,\n tuplet,\n} from '../core/meter/index.js';\nimport { copyTimeSignature } from './shared.js';\n\n/**\n * An immutable time signature: a numerator over a note-value denominator, with\n * the optional grouping that gives an additive metre its felt beats. A thin\n * convenience wrapper over the meter module's plain time signature.\n *\n * One signature, not a piece's meter over time: a piece that changes meter is\n * described by a meter map, which the meter module's own functions read.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Meter } from '@libraz/libcantus';\n * Meter.parse('6/8').isCompound; // true\n * ```\n */\nexport class Meter {\n readonly #ts: TimeSignature;\n\n private constructor(ts: TimeSignature) {\n this.#ts = copyTimeSignature(ts);\n }\n\n /**\n * Build a meter from its parts.\n *\n * @param numerator Beats in a bar, counted in denominator units.\n * @param denominator The note value one unit is written as, e.g. 8 for an\n * eighth note.\n * @param grouping Optional grouping of the bar into felt beats, as positive\n * integers summing to the pulse count or to the numerator — `[2, 2, 3]`\n * for a 2+2+3 reading of 7/8.\n * @returns The meter.\n * @throws If the numerator or denominator is not a positive integer, or the\n * grouping sums to neither the pulse count nor the numerator.\n */\n static of(numerator: number, denominator: number, grouping?: number[]): Meter {\n return new Meter(\n grouping === undefined ? { numerator, denominator } : { numerator, denominator, grouping },\n );\n }\n\n /**\n * Parse a time signature such as `'4/4'`, `'6/8'`, or the additive `'2+2+3/8'`.\n *\n * @param text The signature text.\n * @returns The meter that text names.\n * @throws If the text is not `n/d` with positive integers, or names a\n * signature the library rejects. Use {@link Meter.tryParse} where failure\n * is ordinary, such as a meter field read on every keystroke.\n */\n static parse(text: string): Meter {\n return new Meter(parseTimeSignature(text));\n }\n\n /**\n * Parse a time signature, reporting failure instead of throwing it.\n *\n * The same reading as {@link Meter.parse} — that method is this one with its\n * error thrown — for the callers where text that does not parse yet is the\n * normal state of the input rather than a fault.\n *\n * @param text The signature text.\n * @returns The meter, or the error explaining why the text is not one.\n * @example\n * ```ts\n * import { Meter } from '@libraz/libcantus';\n * const result = Meter.tryParse('7/8');\n * result.ok ? result.value.pulsesPerBar : result.error.message; // 7\n * ```\n */\n static tryParse(text: string): ParseResult<Meter> {\n const parsed = tryParseTimeSignature(text);\n return parsed.ok ? { ok: true, value: new Meter(parsed.value) } : parsed;\n }\n\n /**\n * Wrap a plain time signature, as the meter module produces it.\n *\n * @param data The plain time signature.\n * @returns The wrapped meter.\n * @throws If the signature is not one the library accepts.\n */\n static fromData(data: TimeSignature): Meter {\n return new Meter(data);\n }\n\n /**\n * Rebuild a meter from its {@link Meter.toJSON} output.\n *\n * @param data The serialized time signature.\n * @returns The wrapped meter.\n * @throws If the signature is not one the library accepts.\n */\n static fromJSON(data: TimeSignature): Meter {\n return new Meter(data);\n }\n\n /** Beats in a bar, counted in denominator units: the 6 of 6/8. */\n get numerator(): number {\n return this.#ts.numerator;\n }\n\n /** The note value one unit is written as: the 8 of 6/8. */\n get denominator(): number {\n return this.#ts.denominator;\n }\n\n /** A copy of the felt-beat grouping, or undefined when the bar is ungrouped. */\n get grouping(): number[] | undefined {\n return this.#ts.grouping === undefined ? undefined : [...this.#ts.grouping];\n }\n\n /** A copy of the underlying plain time signature. */\n get data(): TimeSignature {\n return this.toJSON();\n }\n\n /**\n * Whether the meter is compound: its main pulses each divide into three, as\n * in 6/8, 9/8, or 12/8. A simple triple such as 3/4 is not compound, and\n * neither is a signature whose grouping selects the additive reading.\n */\n get isCompound(): boolean {\n return isCompound(this.#ts);\n }\n\n /** Length of a bar in quarter-note beats. */\n get beatsPerBar(): number {\n return beatsPerBar(this.#ts);\n }\n\n /** Main pulses (felt beats) in a bar: 3 in 6/8, 4 in 4/4. */\n get pulsesPerBar(): number {\n return pulsesPerBar(this.#ts);\n }\n\n /**\n * Length of one felt beat in quarter-note beats: a quarter note in simple\n * meters, a dotted quarter in compound ones.\n */\n get pulseBeats(): number {\n return pulseBeats(this.#ts);\n }\n\n /**\n * Render the signature as `'n/d'`, or as `'a+b+c/d'` when it carries an\n * additive grouping and one is asked for.\n *\n * @param opts Set `grouping: true` to render an additive grouping. A\n * grouping counted in main pulses cannot be written additively and falls\n * back to the plain form.\n * @returns The formatted signature.\n * @example\n * ```ts\n * import { Meter } from '@libraz/libcantus';\n * Meter.of(7, 8, [2, 2, 3]).format({ grouping: true }); // '2+2+3/8'\n * ```\n */\n format(opts: { grouping?: boolean } = {}): string {\n return formatTimeSignature(this.#ts, opts);\n }\n\n /**\n * Metric weight of a position within its bar, on a 0–3 scale: 3 the\n * downbeat, 2 a secondary strong pulse, 1 any other main pulse, and 0 an\n * off-pulse subdivision.\n *\n * @param beatInQuarters Position in quarter-note beats; a position past the\n * end of a bar is read in the bar it falls in.\n * @returns The metric weight (0–3).\n * @throws If the position is not finite.\n */\n weightAt(beatInQuarters: number): number {\n return metricWeight(beatInQuarters, this.#ts);\n }\n\n /**\n * Whether a position is metrically accented — a downbeat or a secondary\n * strong pulse.\n *\n * @param beatInQuarters Position in quarter-note beats.\n * @returns True on strong beats.\n * @throws If the position is not finite.\n */\n isStrongBeat(beatInQuarters: number): boolean {\n return isStrongBeat(beatInQuarters, this.#ts);\n }\n\n /**\n * Convert a position in quarter-note beats to a bar index and in-bar offset.\n *\n * @param beatInQuarters Position in quarter-note beats.\n * @returns The 0-based bar and the quarter-note offset within it.\n * @throws If the position is not finite.\n */\n barPositionAt(beatInQuarters: number): BarPosition {\n return beatToBarPosition(beatInQuarters, this.#ts);\n }\n\n /**\n * Render a position as the `bar.beat` a DAW or a score shows: 1-based bar,\n * 1-based felt beat.\n *\n * @param beatInQuarters Position in quarter-note beats.\n * @param decimals Digits of the fractional beat to keep.\n * @returns The formatted position, e.g. `'3.2'` for bar 3, felt beat 2.\n * @throws If the position is not finite or `decimals` is not an integer in\n * 0..100.\n * @example\n * ```ts\n * import { Meter } from '@libraz/libcantus';\n * Meter.parse('6/8').formatPosition(7.5); // '3.2'\n * ```\n */\n formatPosition(beatInQuarters: number, decimals = 2): string {\n return formatBarPosition(beatInQuarters, this.#ts, decimals);\n }\n\n /**\n * Subdivide a span into equal tuplet durations — an eighth-note triplet is\n * `tuplet(1, 3)`.\n *\n * @param totalBeats Total span in quarter-note beats.\n * @param count Number of equal parts.\n * @returns `count` equal durations summing to `totalBeats`.\n * @throws If the span is negative or `count` is not a positive integer.\n */\n tuplet(totalBeats: number, count: number): number[] {\n return tuplet(totalBeats, count);\n }\n\n /**\n * Whether another meter is the same signature, grouping included.\n *\n * 6/8 and 3/4 fill a bar with the same six eighth notes and are not equal:\n * they group them differently, and every weight this class reports follows\n * the grouping.\n *\n * @param other The meter to compare with.\n * @returns True when numerator, denominator, and grouping all match.\n */\n equals(other: Meter): boolean {\n // The other meter is read through its public surface rather than its\n // private field: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n const b = other.data;\n const grouping = this.#ts.grouping;\n if (grouping === undefined || b.grouping === undefined) {\n return (\n this.#ts.numerator === b.numerator &&\n this.#ts.denominator === b.denominator &&\n grouping === b.grouping\n );\n }\n return (\n this.#ts.numerator === b.numerator &&\n this.#ts.denominator === b.denominator &&\n grouping.length === b.grouping.length &&\n grouping.every((entry, index) => entry === b.grouping?.[index])\n );\n }\n\n /**\n * The plain time signature, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(meter)` from collapsing to `{}`. The grouping appears only\n * on a meter that carries one, which is the shape the meter module's own\n * parser produces.\n *\n * @returns A copy of the numerator, denominator, and grouping.\n */\n toJSON(): TimeSignature {\n return copyTimeSignature(this.#ts);\n }\n\n /**\n * The signature, so a template literal or a log line reads as the meter.\n *\n * @returns The plain form, e.g. `'6/8'`; ask {@link Meter.format} for the\n * additive one.\n */\n toString(): string {\n return this.format();\n }\n}\n","import type { MotifRelation } from '../analyze/melody/index.js';\nimport { melodicSimilarity, motifFromNotes, relateMotifs } from '../analyze/melody/index.js';\nimport type { ChordTimeline } from '../analyze/timeline/index.js';\nimport { InvalidInputError } from '../core/errors/index.js';\nimport type { TimeSignature } from '../core/meter/index.js';\nimport type { NoteEvent } from '../core/types.js';\nimport { assertNoteEvent } from '../core/validation/index.js';\nimport type {\n GenerationContextInput,\n MotifCell,\n MotifContour,\n MotifNote,\n MotifOptions,\n MotifTransform,\n} from '../generate/index.js';\nimport {\n developMotif,\n generateMotif,\n motifToNoteEvents,\n transformMotif,\n} from '../generate/index.js';\nimport { type KeyLike, toKeyScale } from '../theory/scale/index.js';\nimport { type ChordLike, toChordData } from '../theory/symbol/index.js';\nimport type { ScoreOptions } from './score.js';\nimport { Score } from './score.js';\nimport { withoutNegativeZero } from './shared.js';\nimport type { Timeline } from './timeline.js';\n\n/**\n * How a motif is generated: what {@link MotifOptions} asks for, with the key\n * and the chord taken the way the rest of the class API takes them.\n */\nexport type MotifGenerateOptions = {\n /** The key the line is written in: a name, plain key data, or a {@link Key}. */\n key: KeyLike;\n /**\n * The chord the downbeats spell out, when the line is written over one: a\n * chord symbol, plain chord data, or a {@link Chord}. Without it no note is\n * pulled to a chord tone.\n */\n chord?: ChordLike | null;\n /** Length of the motif in bars of `ts`. */\n bars: number;\n /**\n * Meter the bars are counted in, which sets the bar length.\n *\n * @defaultValue 4/4\n */\n ts?: TimeSignature;\n /**\n * Melodic contour shape the line follows.\n *\n * @defaultValue `'arch'`\n */\n contour?: MotifContour;\n /**\n * Probability in [0, 1] that a note is nudged by a single diatonic step;\n * sugar for the context's `complexity.ornament`, which wins where both are\n * given.\n *\n * @defaultValue 0\n */\n jitter?: number;\n /**\n * The generation context. Its `complexity.ornament` is the jitter and its\n * `seed` fixes which notes are nudged.\n *\n * @defaultValue `{ seed: 0 }`\n */\n ctx?: GenerationContextInput;\n};\n\n/** Defensive copy of one note, carrying only the fields a motif note holds. */\nfunction copyNote(note: MotifNote): MotifNote {\n return {\n pitch: withoutNegativeZero(note.pitch),\n startBeat: withoutNegativeZero(note.startBeat),\n durationBeat: withoutNegativeZero(note.durationBeat),\n };\n}\n\n/**\n * Defensive copy of a cell, its notes checked as they are copied.\n *\n * A note that never sounds is refused, which is the policy the transforms\n * themselves hold: a cell is written material rather than an imported track,\n * and a zero-length note in one would be tiled and transformed as though it\n * were a note.\n *\n * The notes keep the order they arrive in rather than being sorted into time\n * order: the transforms read the first note of the array as the cell's pivot\n * and its head, and a retrograde is exactly the cell whose notes no longer run\n * forwards.\n */\nfunction copyCell(cell: MotifCell): MotifCell {\n if (!Array.isArray(cell.notes)) {\n throw new InvalidInputError(`motif notes must be an array; received ${typeof cell.notes}`);\n }\n return {\n notes: cell.notes.map((note, index) => {\n if (note === undefined) {\n throw new InvalidInputError(`motif notes[${index}] must be a note; received undefined`);\n }\n return copyNote(assertNoteEvent(note, `motif notes[${index}]`));\n }),\n };\n}\n\n/** The chord data a {@link MotifGenerateOptions.chord} value names, if any. */\nfunction chordFrom(chord: ChordLike | null | undefined): MotifOptions['chord'] {\n return chord === undefined || chord === null ? chord : toChordData(chord);\n}\n\n/**\n * A short melodic cell, and the transformations that make a piece out of one.\n *\n * A motif is the smallest thing a composer works with: a handful of notes that\n * come back inverted, in retrograde, a step higher, twice as slow. The library\n * generates one and transforms one, but every step hands back a bare cell that\n * has to be named again on the way into the next, and the key travels beside it\n * by hand. A `Motif` carries the cell, so a subject and its answer are two\n * expressions rather than two variables.\n *\n * The cell holds notes and nothing else: the key it was written in is not part\n * of it, since the same figure is worth restating in another key, and the\n * members that need one are given it.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Motif } from '@libraz/libcantus';\n * const subject = Motif.generate({ key: 'C major', bars: 1 });\n * subject.transform('invert').relateTo(subject)?.kind; // 'inversion'\n * ```\n */\nexport class Motif {\n readonly #cell: MotifCell;\n\n /**\n * Wrap a plain motif cell.\n *\n * @param cell The notes of the cell; copied, never retained.\n * @throws If a note carries a value the transforms cannot hold.\n */\n constructor(cell: MotifCell) {\n this.#cell = copyCell(cell);\n }\n\n /**\n * Generate a seed motif.\n *\n * The line follows the requested contour in diatonic steps from the tonic,\n * snapped to the key; with a chord in hand, the notes landing on bar\n * downbeats are pulled to the nearest chord tone.\n *\n * @param opts Key, chord, length, contour, jitter and context; see\n * {@link MotifGenerateOptions}.\n * @returns The generated motif.\n * @throws If the key or the chord names none, or a dial is outside [0, 1].\n * @example\n * ```ts\n * import { Motif } from '@libraz/libcantus';\n * const motif = Motif.generate({ key: 'C major', bars: 2, contour: 'ascending' });\n * motif.notes.length; // 4\n * ```\n */\n static generate(opts: MotifGenerateOptions): Motif {\n return new Motif(\n generateMotif({ ...opts, key: toKeyScale(opts.key), chord: chordFrom(opts.chord) }),\n );\n }\n\n /**\n * Read a run of notes as a motif.\n *\n * This is how a caller names a cell it already has — a subject lifted out of\n * a score, a phrase a player typed in. Only the fields a motif note carries\n * are kept, so notes that arrived from a score leave their dynamics behind.\n *\n * @param notes The cell, in the order the transforms should read it.\n * @returns The motif.\n * @throws If a note carries a value the transforms cannot hold.\n */\n static fromNotes(notes: readonly NoteEvent[]): Motif {\n return new Motif({ notes: [...notes] });\n }\n\n /** Rebuild a motif from the plain data {@link Motif.data} hands out. */\n static fromData(cell: MotifCell): Motif {\n return new Motif(cell);\n }\n\n /** Rebuild a motif from its {@link Motif.toJSON} output. */\n static fromJSON(cell: MotifCell): Motif {\n return new Motif(cell);\n }\n\n /** The notes of the cell, in the order the transforms read them. */\n get notes(): MotifNote[] {\n return this.#cell.notes.map(copyNote);\n }\n\n /** Where the cell stops sounding, measured from its own first onset. */\n get totalBeats(): number {\n if (this.#cell.notes.length === 0) {\n return 0;\n }\n const start = Math.min(...this.#cell.notes.map((note) => note.startBeat));\n const end = Math.max(...this.#cell.notes.map((note) => note.startBeat + note.durationBeat));\n return end - start;\n }\n\n /** A copy of the underlying plain cell. */\n get data(): MotifCell {\n return { notes: this.#cell.notes.map(copyNote) };\n }\n\n /**\n * The cell put through one of the classical transformations.\n *\n * `invert` reflects the pitches about the first note and `retrograde` mirrors\n * the onsets about the cell's span, both self-inverse; `augment` and\n * `diminish` scale the note values by `amount` and its reciprocal;\n * `transposeChromatic` moves by semitones; `transposeDiatonic` and `sequence`\n * move by scale degrees with a key in hand, and by semitones without one.\n *\n * @param kind The transformation.\n * @param amount Semitones, scale degrees, or the time factor, as the\n * transformation reads it.\n * @param key Key context for the diatonic transformations: a name, plain key\n * data, or a {@link Key}.\n * @returns The transformed motif.\n * @throws If a transformed pitch would leave the MIDI range.\n * @example\n * ```ts\n * import { Motif } from '@libraz/libcantus';\n * const motif = Motif.fromNotes([\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * ]);\n * motif.transform('transposeDiatonic', 1, 'C major').notes[0]?.pitch; // 62\n * ```\n */\n transform(kind: MotifTransform, amount?: number, key?: KeyLike): Motif {\n return new Motif(\n transformMotif(this.#cell, kind, amount, key === undefined ? undefined : toKeyScale(key)),\n );\n }\n\n /**\n * The cell laid across a span and snapped to the harmony under it.\n *\n * The cell is tiled back to back to fill the bars asked for; the notes\n * carrying structural weight are pulled to the nearest chord tone of the\n * segment sounding there, and the ones between them are kept in the key.\n *\n * @param timeline The harmony to snap against, as a {@link Timeline} or the\n * plain chord timeline the analysis layer hands out.\n * @param key The key the passing notes are kept in.\n * @param bars How many bars to fill.\n * @param ts Meter the bars are counted in; 4/4 when none is named.\n * @returns The developed motif.\n * @throws If the bar count is not a positive integer.\n */\n develop(\n timeline: Timeline | ChordTimeline,\n key: KeyLike,\n bars: number,\n ts?: TimeSignature,\n ): Motif {\n // Read through the public surface rather than by `instanceof`, so a\n // timeline built by a second copy of the module develops like any other.\n const chords = 'chordTimeline' in timeline ? timeline.chordTimeline : timeline;\n return new Motif(developMotif(this.#cell, chords, toKeyScale(key), bars, ts));\n }\n\n /**\n * How this motif stands to another: the transformation that turns one into\n * the other, named.\n *\n * @param other The statement to name against this one.\n * @param key Key context for the tonal reading; without it, an answer that\n * only holds diatonically is not recognised.\n * @returns The relation, or null when the two stand in none.\n */\n relateTo(other: Motif, key?: KeyLike): MotifRelation | null {\n return relateMotifs(\n motifFromNotes(this.#cell.notes),\n motifFromNotes(other.notes),\n key === undefined ? undefined : toKeyScale(key),\n );\n }\n\n /**\n * How alike two lines are, in [0, 1].\n *\n * What {@link Motif.relateTo} answers null for still scores here: this is the\n * measure for a variant that adds a passing note or drops one, rather than\n * for the exact transformations.\n *\n * @param other The line to compare with.\n * @returns The likeness: 1 for the same line, a transposition of it included.\n */\n similarityTo(other: Motif): number {\n return melodicSimilarity(this.#cell.notes, other.notes);\n }\n\n /**\n * The cell as a score, so it reaches the rest of the class API.\n *\n * @param opts The meter, tempo and key to read the notes against; see\n * {@link ScoreOptions}.\n * @returns The score holding the cell.\n */\n toScore(opts?: ScoreOptions): Score {\n return Score.of(motifToNoteEvents(this.#cell), opts);\n }\n\n /**\n * Whether another motif holds the same notes in the same order.\n *\n * The comparison is made through the other motif's public data, so two\n * motifs built by different copies of the module still compare. The order is\n * part of it: the transformations read the first note of the cell as its\n * pivot, so two cells sounding alike but read from different ends are not\n * the same motif.\n *\n * @param other The motif to compare.\n * @returns True when the cells hold the same notes in the same order.\n */\n equals(other: Motif): boolean {\n const theirs = other.data;\n return (\n this.#cell.notes.length === theirs.notes.length &&\n this.#cell.notes.every((note, index) => sameNote(note, theirs.notes[index]))\n );\n }\n\n /** The plain form of the cell, for `JSON.stringify`. */\n toJSON(): MotifCell {\n return this.data;\n }\n}\n\n/** Whether two notes sound the same pitch over the same stretch. */\nfunction sameNote(note: MotifNote, other: MotifNote | undefined): boolean {\n return (\n other !== undefined &&\n note.pitch === other.pitch &&\n note.startBeat === other.startBeat &&\n note.durationBeat === other.durationBeat\n );\n}\n","import { InvalidInputError } from '../core/errors/index.js';\nimport type { TimeSignature } from '../core/meter/index.js';\nimport { assertRange } from '../core/validation/index.js';\nimport type {\n DeformOptions,\n GenerationContextInput,\n GridEvent,\n RhythmEvent,\n RhythmOptions,\n} from '../generate/index.js';\nimport {\n deform,\n doubleTime,\n generateRhythm,\n halfTime,\n ornamentBy,\n resolveContext,\n rhythmDensity,\n rhythmToNoteEvents,\n STEP_BEATS,\n syncopate,\n thin,\n withinCeiling,\n} from '../generate/index.js';\nimport { Score } from './score.js';\nimport { copyTimeSignature, withoutNegativeZero } from './shared.js';\n\n/** The plain form a {@link Rhythm} hands out and is rebuilt from. */\nexport type RhythmData = {\n /** The onsets, in time order, measured in quarter-note beats. */\n events: RhythmEvent[];\n /** The meter the onsets are counted in. */\n ts: TimeSignature;\n};\n\n/** What {@link Rhythm.deform} does to a pattern beyond what the dials say. */\nexport type RhythmDeformOptions = {\n /**\n * Stretch or compress the pattern before the dials are applied, as\n * {@link DeformOptions.rate} does.\n */\n rate?: 'straight' | 'half' | 'double';\n /**\n * Which onsets count as decoration, for the ornament dial. A rhythm carries\n * no dynamics of its own, so what counts is the caller's reading of where the\n * onsets fall.\n */\n isOrnament?: (event: RhythmEvent) => boolean;\n};\n\n/** The meter a rhythm is read in when the caller names none. */\nconst DEFAULT_TS: TimeSignature = { numerator: 4, denominator: 4 };\n\n/**\n * The loudness every onset enters the sixteenth grid at.\n *\n * The transforms play an added note softer than the one it follows, which is a\n * statement about dynamics; a rhythm is onsets alone, so they all enter alike\n * and the scaling is dropped again on the way back.\n */\nconst GRID_VELOCITY = 1;\n\n/** The part name the pattern's draws are addressed under, as the generator's are. */\nconst PART = 'rhythm';\n\n/** A grid onset that remembers the pattern onset it was built from. */\ntype SourcedGridEvent = GridEvent & { source: RhythmEvent };\n\n/** Defensive copy of one onset, checked as it is copied. */\nfunction copyEvent(event: RhythmEvent, name: string): RhythmEvent {\n return {\n position: withoutNegativeZero(\n assertRange(event.position, 0, Number.MAX_SAFE_INTEGER, `${name}.position`),\n ),\n duration: withoutNegativeZero(\n assertRange(event.duration, 0, Number.MAX_SAFE_INTEGER, `${name}.duration`),\n ),\n };\n}\n\n/**\n * The onsets checked and copied, in time order.\n *\n * A rhythm is asked what falls where rather than what arrived first, so the\n * order is settled here: every transform below, and every `equals` between two\n * patterns holding the same onsets, then reads the same sequence.\n */\nfunction copyEvents(events: readonly RhythmEvent[], name: string): RhythmEvent[] {\n if (!Array.isArray(events)) {\n throw new InvalidInputError(`${name} must be an array; received ${typeof events}`);\n }\n return events\n .map((event, index) => {\n if (event === undefined) {\n throw new InvalidInputError(`${name}[${index}] must be an onset; received undefined`);\n }\n return copyEvent(event, `${name}[${index}]`);\n })\n .sort((a, b) => a.position - b.position || a.duration - b.duration);\n}\n\n/** Defensive copy of plain rhythm data, with every part validated. */\nfunction copyRhythm(data: RhythmData): RhythmData {\n return {\n events: copyEvents(data.events, 'rhythm events'),\n ts: copyTimeSignature(data.ts, 'rhythm ts'),\n };\n}\n\n/** Where the pattern stops: the furthest point any onset sounds to. */\nfunction spanOf(events: readonly RhythmEvent[]): number {\n return events.reduce((end, event) => Math.max(end, event.position + event.duration), 0);\n}\n\n/**\n * The pattern's onsets on the sixteenth grid the transforms are written for.\n *\n * A position that is not a whole sixteenth — a triplet, anything generated at\n * another subdivision — keeps its exact place and is read as the off-grid, and\n * so weakest, position it is.\n */\nfunction toGrid(events: readonly RhythmEvent[]): SourcedGridEvent[] {\n return events.map((event) => ({\n step: event.position / STEP_BEATS,\n velocity: GRID_VELOCITY,\n source: event,\n }));\n}\n\n/**\n * The onsets a transform returned, read back as a pattern over the same span.\n *\n * Durations are re-read from the onsets rather than carried through the\n * transform, exactly as the generator writes them: each onset sounds until the\n * next, and the last to the end of the span the pattern covered before the\n * transform. Two onsets landing on one position are one onset, since a rhythm\n * has nothing to tell them apart with.\n */\nfunction fromGrid(events: readonly GridEvent[], spanBeats: number): RhythmEvent[] {\n const positions = [...new Set(events.map((event) => event.step * STEP_BEATS))].sort(\n (a, b) => a - b,\n );\n return positions.map((position, index) => ({\n position,\n duration: (positions[index + 1] ?? spanBeats) - position,\n }));\n}\n\n/**\n * A pattern of onsets over a meter, and the transformations that reshape one.\n *\n * The library generates a rhythm as a bare array and deforms one with a family\n * of functions — thinning, syncopating, halving and doubling the rate — that\n * each take an array and hand one back. They are shaped to chain and cannot:\n * every step has to name the pattern again, and the meter it is counted in\n * travels beside it by hand. A `Rhythm` holds the two together, so the family\n * reads as the chain it always was.\n *\n * The transformations are written against a sixteenth grid, so the onsets are\n * converted to it and read back afterwards. A rhythm carries no dynamics, and\n * the softer stroke a transform gives an added note is not part of what comes\n * back.\n *\n * @category Class API\n * @example\n * ```ts\n * import { parseTimeSignature } from '@libraz/libcantus';\n * import { Rhythm } from '@libraz/libcantus';\n * const rhythm = Rhythm.generate(parseTimeSignature('4/4'), { ctx: { seed: 42 } });\n * rhythm.syncopate(0.3).toScore(38).notes.length >= rhythm.events.length; // true\n * ```\n */\nexport class Rhythm {\n readonly #data: RhythmData;\n\n /**\n * Wrap plain rhythm data.\n *\n * @param data The onsets and the meter they are counted in; copied, never\n * retained.\n * @throws If an onset or the meter carries a value a pattern cannot hold.\n */\n constructor(data: RhythmData) {\n this.#data = copyRhythm(data);\n }\n\n /**\n * Generate a pattern over a meter.\n *\n * @param ts The time signature the bars are counted in.\n * @param opts Length, grid resolution and the context; see\n * {@link RhythmOptions}. The context's `complexity.rhythmic` is the onset\n * density, and its `seed` fixes which grid slots are taken.\n * @returns The generated pattern.\n * @example\n * ```ts\n * import { parseTimeSignature } from '@libraz/libcantus';\n * import { Rhythm } from '@libraz/libcantus';\n * const rhythm = Rhythm.generate(parseTimeSignature('4/4'), {\n * bars: 2,\n * ctx: { seed: 7, complexity: { rhythmic: 0.6 } },\n * });\n * rhythm.totalBeats; // 8\n * ```\n */\n static generate(ts: TimeSignature, opts?: RhythmOptions): Rhythm {\n return new Rhythm({ events: generateRhythm(ts, opts), ts });\n }\n\n /**\n * Build a pattern from onsets that already exist.\n *\n * @param events The onsets, in any order.\n * @param ts The meter they are counted in; 4/4 when none is named, as every\n * meter-aware function in the library assumes.\n * @returns The pattern.\n */\n static of(events: readonly RhythmEvent[], ts: TimeSignature = DEFAULT_TS): Rhythm {\n return new Rhythm({ events: [...events], ts });\n }\n\n /** Rebuild a pattern from the plain data {@link Rhythm.data} hands out. */\n static fromData(data: RhythmData): Rhythm {\n return new Rhythm(data);\n }\n\n /** Rebuild a pattern from its {@link Rhythm.toJSON} output. */\n static fromJSON(data: RhythmData): Rhythm {\n return new Rhythm(data);\n }\n\n /** The onsets, in time order. */\n get events(): RhythmEvent[] {\n return this.#data.events.map((event) => ({ ...event }));\n }\n\n /** The meter the onsets are counted in. */\n get ts(): TimeSignature {\n return copyTimeSignature(this.#data.ts, 'rhythm ts');\n }\n\n /** Where the pattern stops: the furthest point any onset sounds to. */\n get totalBeats(): number {\n return spanOf(this.#data.events);\n }\n\n /** A copy of the underlying plain data. */\n get data(): RhythmData {\n return {\n events: this.#data.events.map((event) => ({ ...event })),\n ts: copyTimeSignature(this.#data.ts, 'rhythm ts'),\n };\n }\n\n /**\n * How busy the pattern is: the mean number of onsets per bar.\n *\n * @returns The onset count divided by the number of bars it spans.\n */\n density(): number {\n return rhythmDensity(this.#data.events, this.#data.ts);\n }\n\n /**\n * Drop the onsets carrying the least of the metre.\n *\n * @param amount How much to thin, in [0, 1]; at 0 nothing goes, and a full\n * turn leaves the downbeats alone.\n * @returns The thinned pattern.\n * @throws If the amount is outside [0, 1].\n */\n thin(amount: number): Rhythm {\n return this.#withGrid(thin(toGrid(this.#data.events), amount));\n }\n\n /**\n * Anticipate beats, one sixteenth early.\n *\n * The anticipations are added rather than displaced, so raising the amount\n * only ever adds onsets and the ones already sounding stay where they are.\n *\n * @param amount How much syncopation, in [0, 1].\n * @param ctx The context, or the seed alone; it fixes which beats are\n * anticipated.\n * @returns The syncopated pattern.\n * @throws If the amount is outside [0, 1].\n */\n syncopate(amount: number, ctx?: GenerationContextInput): Rhythm {\n return this.#withGrid(\n syncopate(toGrid(this.#data.events), amount, resolveContext(ctx).part(PART)),\n );\n }\n\n /**\n * Compress the pattern to half its length and play it twice.\n *\n * The span is the pattern's own, so it stays as full as it was.\n *\n * @returns The compressed pattern.\n */\n doubleTime(): Rhythm {\n return this.#withGrid(doubleTime(toGrid(this.#data.events), this.#spanSteps()));\n }\n\n /**\n * Stretch the pattern to twice its length: everything falls half as often.\n *\n * Onsets stretched past the span are dropped, since the span is the span\n * however the pattern is felt.\n *\n * @returns The stretched pattern.\n */\n halfTime(): Rhythm {\n return this.#withGrid(halfTime(toGrid(this.#data.events), this.#spanSteps()));\n }\n\n /**\n * Keep as much decoration as the ornament dial asks for.\n *\n * Each decorated onset survives on its own draw, so raising the amount brings\n * more of them back without disturbing the ones already sounding. Onsets the\n * predicate does not claim are untouched.\n *\n * @param isOrnament Which onsets count as decoration.\n * @param amount The ornament dial, in [0, 1].\n * @param ctx The context, or the seed alone; it fixes which decoration stays.\n * @returns The pattern with the surviving decoration.\n * @throws If the amount is outside [0, 1].\n */\n ornamentBy(\n isOrnament: (event: RhythmEvent) => boolean,\n amount: number,\n ctx?: GenerationContextInput,\n ): Rhythm {\n return this.#withGrid(\n ornamentBy(\n toGrid(this.#data.events),\n (grid) => isOrnament(grid.source),\n amount,\n resolveContext(ctx).part(PART),\n ),\n );\n }\n\n /**\n * Apply the complexity dials to the pattern in one pass.\n *\n * The dials are read from the context, which is where they live for every\n * generator: `complexity.rhythmic` below its neutral middle thins the pattern\n * and above it syncopates, and `complexity.ornament` decides how much of what\n * `isOrnament` claims survives. The span the rate is measured against is the\n * pattern's own.\n *\n * @param opts The rate, and which onsets are decoration; see\n * {@link RhythmDeformOptions}.\n * @param ctx The context, or the seed alone; it carries the dials.\n * @returns The deformed pattern.\n * @throws If a dial is outside [0, 1].\n */\n deform(opts?: RhythmDeformOptions, ctx?: GenerationContextInput): Rhythm {\n const resolved = resolveContext(ctx);\n const isOrnament = opts?.isOrnament;\n const options: DeformOptions = {\n rhythmic: resolved.rhythmic,\n ornament: resolved.ornament,\n rate: opts?.rate,\n spanSteps: this.#spanSteps(),\n // The transform hands the predicate the events it was given, which carry\n // the onset each was built from; the parameter is declared over the bare\n // grid event the rule reads, so the field is named back here.\n isOrnament:\n isOrnament === undefined\n ? undefined\n : (grid) => isOrnament((grid as SourcedGridEvent).source),\n };\n return this.#withGrid(deform(toGrid(this.#data.events), options, resolved.part(PART)));\n }\n\n /**\n * Whether one player can sustain the pattern at the context's tempo.\n *\n * The ceiling is measured against the closest pair of onsets, which is what\n * actually stops a hand. A context naming no tempo or no ceiling has nothing\n * to measure against, and the pattern passes.\n *\n * @param ctx The context, or the seed alone; its `bpm` and its\n * `complexity.difficulty` are what the answer is read from.\n * @returns True when the pattern is inside the ceiling.\n */\n withinCeiling(ctx?: GenerationContextInput): boolean {\n const resolved = resolveContext(ctx);\n return withinCeiling(toGrid(this.#data.events), resolved.bpm, resolved.difficulty);\n }\n\n /**\n * The pattern as sounding notes at one pitch, in its own meter.\n *\n * This is where a rhythm reaches the rest of the class API: the score it\n * hands back can be humanized, grooved, analysed, or written out like any\n * other line.\n *\n * @param pitch The MIDI pitch to give every onset.\n * @param velocity Velocity for every onset; a mezzo-forte 96 by default.\n * @returns The score holding the pattern.\n * @throws If the pitch is not finite or the velocity is outside [0, 127].\n * @example\n * ```ts\n * import { parseTimeSignature } from '@libraz/libcantus';\n * import { Rhythm } from '@libraz/libcantus';\n * const score = Rhythm.generate(parseTimeSignature('4/4')).toScore(38);\n * score.notes.every((note) => note.pitch === 38); // true\n * ```\n */\n toScore(pitch: number, velocity?: number): Score {\n return Score.of(rhythmToNoteEvents(this.#data.events, pitch, velocity), {\n meters: this.#data.ts,\n });\n }\n\n /**\n * Whether another pattern holds the same onsets in the same meter.\n *\n * The comparison is made through the other pattern's public data, so two\n * patterns built by different copies of the module still compare.\n *\n * @param other The pattern to compare.\n * @returns True when the onsets and the meter match.\n */\n equals(other: Rhythm): boolean {\n const theirs = other.data;\n const mine = this.#data;\n return (\n mine.events.length === theirs.events.length &&\n mine.events.every((event, index) => sameEvent(event, theirs.events[index])) &&\n sameSignature(mine.ts, theirs.ts)\n );\n }\n\n /** The plain form of the pattern, for `JSON.stringify`. */\n toJSON(): RhythmData {\n return this.data;\n }\n\n /** The pattern's span as a count of sixteenths, for the transforms. */\n #spanSteps(): number {\n return spanOf(this.#data.events) / STEP_BEATS;\n }\n\n /** The same meter carrying the onsets a transform returned. */\n #withGrid(events: readonly GridEvent[]): Rhythm {\n return new Rhythm({\n events: fromGrid(events, spanOf(this.#data.events)),\n ts: this.#data.ts,\n });\n }\n}\n\n/** Whether two onsets fall at the same place for the same length. */\nfunction sameEvent(event: RhythmEvent, other: RhythmEvent | undefined): boolean {\n return (\n other !== undefined && event.position === other.position && event.duration === other.duration\n );\n}\n\n/** Whether two signatures name the same meter, grouping included. */\nfunction sameSignature(ts: TimeSignature, other: TimeSignature): boolean {\n const grouping = ts.grouping ?? [];\n const theirs = other.grouping ?? [];\n return (\n ts.numerator === other.numerator &&\n ts.denominator === other.denominator &&\n grouping.length === theirs.length &&\n grouping.every((entry, index) => entry === theirs[index])\n );\n}\n","import {\n beatsToSeconds,\n beatsToTicks,\n durationToSeconds,\n secondsToBeats,\n ticksToBeats,\n} from '../core/tempo/index.js';\nimport { assertRange } from '../core/validation/index.js';\n\n/**\n * Upper bound on a tempo, the bound the tempo module and the generators\n * accept. A faster marking is a notation of a slower pulse, not a distinct\n * tempo.\n */\nconst MAX_BPM = 1000;\n\n/**\n * A constant tempo as plain data: quarter-note beats per minute.\n *\n * @category Class API\n */\nexport type TempoData = {\n /** Quarter-note beats per minute. */\n bpm: number;\n};\n\n/**\n * An immutable constant tempo, in quarter-note beats per minute, and the\n * conversions it settles: beats to wall-clock seconds and back, and beats to\n * MIDI ticks and back.\n *\n * One tempo, not a piece's tempo over time: a piece that changes tempo is\n * described by a tempo map, which the tempo module's own functions read.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Tempo } from '@libraz/libcantus';\n * Tempo.of(120).secondsAt(16); // 8\n * ```\n */\nexport class Tempo {\n readonly #bpm: number;\n\n private constructor(bpm: number) {\n this.#bpm = assertRange(bpm, Number.MIN_VALUE, MAX_BPM, 'bpm');\n }\n\n /**\n * The one-event tempo map every conversion here is measured against.\n *\n * Built on read rather than held, so nothing outside the class can reach the\n * array the conversions run over.\n */\n get #map(): readonly { startBeat: number; bpm: number }[] {\n return [{ startBeat: 0, bpm: this.#bpm }];\n }\n\n /**\n * Build a tempo from a beats-per-minute marking.\n *\n * @param bpm Quarter-note beats per minute.\n * @returns The tempo.\n * @throws If the marking is not a positive, finite number the library holds.\n */\n static of(bpm: number): Tempo {\n return new Tempo(bpm);\n }\n\n /**\n * Wrap a plain tempo value.\n *\n * @param data The plain tempo.\n * @returns The wrapped tempo.\n * @throws If the marking is not a positive, finite number the library holds.\n */\n static fromData(data: TempoData): Tempo {\n return new Tempo(data.bpm);\n }\n\n /**\n * Rebuild a tempo from its {@link Tempo.toJSON} output.\n *\n * @param data The serialized tempo.\n * @returns The wrapped tempo.\n * @throws If the marking is not a positive, finite number the library holds.\n */\n static fromJSON(data: TempoData): Tempo {\n return new Tempo(data.bpm);\n }\n\n /** Quarter-note beats per minute. */\n get bpm(): number {\n return this.#bpm;\n }\n\n /** A copy of the underlying plain tempo value. */\n get data(): TempoData {\n return this.toJSON();\n }\n\n /**\n * Wall-clock seconds from the downbeat to a position.\n *\n * A position before the downbeat reports negative elapsed time: a pickup\n * sounds before it, and asking when it sounds is a fair question.\n *\n * @param beat Position in quarter-note beats.\n * @returns Elapsed seconds, 0 at beat 0 and negative before it.\n * @throws If the position is not finite.\n */\n secondsAt(beat: number): number {\n return beatsToSeconds(beat, this.#map);\n }\n\n /**\n * The position reached after a number of seconds — the inverse of\n * {@link Tempo.secondsAt}.\n *\n * @param seconds Elapsed seconds from the downbeat; negative for a position\n * before it, such as a pickup.\n * @returns The position in quarter-note beats.\n * @throws If the elapsed time is not finite.\n * @example\n * ```ts\n * import { Tempo } from '@libraz/libcantus';\n * Tempo.of(90).beatAtSeconds(4); // 6\n * ```\n */\n beatAtSeconds(seconds: number): number {\n return secondsToBeats(seconds, this.#map);\n }\n\n /**\n * Duration in seconds of a span of beats.\n *\n * The tempo is constant, so the span is measured from the downbeat: where it\n * begins cannot change how long it lasts.\n *\n * @param lengthBeats How long the span lasts, in quarter-note beats.\n * @returns The span's duration in seconds.\n * @throws If the length is negative or not finite.\n */\n secondsOf(lengthBeats: number): number {\n return durationToSeconds(0, lengthBeats, this.#map);\n }\n\n /**\n * Convert quarter-note beats to MIDI ticks, rounded to the nearest tick.\n *\n * @param beat Position or length in quarter-note beats.\n * @param ppq Pulses (ticks) per quarter note, e.g. 96, 480, or 960.\n * @returns The whole tick count, negative before the first downbeat.\n * @throws If the position is not finite or `ppq` is not a positive integer.\n */\n ticksAt(beat: number, ppq: number): number {\n return beatsToTicks(beat, ppq);\n }\n\n /**\n * Convert MIDI ticks to quarter-note beats.\n *\n * @param ticks Whole tick count, negative before the first downbeat.\n * @param ppq Pulses (ticks) per quarter note, e.g. 96, 480, or 960.\n * @returns The position or length in quarter-note beats.\n * @throws If `ticks` is not an integer or `ppq` is not a positive integer.\n */\n beatAtTicks(ticks: number, ppq: number): number {\n return ticksToBeats(ticks, ppq);\n }\n\n /**\n * Whether another tempo carries the same marking.\n *\n * @param other The tempo to compare with.\n * @returns True when both name the same beats per minute.\n */\n equals(other: Tempo): boolean {\n // The other tempo is read through its public accessor rather than its\n // private field: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n return this.#bpm === other.bpm;\n }\n\n /**\n * The plain tempo value, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(tempo)` from collapsing to `{}`.\n *\n * @returns A copy of the marking.\n */\n toJSON(): TempoData {\n return { bpm: this.#bpm };\n }\n\n /**\n * The marking, so a template literal or a log line reads as the tempo.\n *\n * @returns The marking, e.g. `'120 bpm'`.\n */\n toString(): string {\n return `${this.#bpm} bpm`;\n }\n}\n","import type { NoteLike } from '../core/pitch/index.js';\nimport { noteToMidi, toNoteData } from '../core/pitch/index.js';\nimport type { TuningTable } from '../core/tuning/index.js';\nimport {\n centsBetweenFreq,\n centsFromNearestStep,\n centsOfSteps,\n centsToRatio,\n edo,\n frequencyOf,\n justDeviationCents,\n nearestStep,\n ratioToCents,\n stepOf,\n stepsOfCents,\n TWELVE_TET,\n} from '../core/tuning/index.js';\n\n/**\n * An immutable equal temperament: a reference pitch and a number of equal\n * divisions of the octave, with the conversions of the tuning module bound to\n * it. A thin convenience wrapper over a plain {@link TuningTable}.\n *\n * Every conversion still takes a temperament of its own, defaulting to this\n * one, so nothing the tuning functions offer is out of reach from the class —\n * two temperaments can be read from a single object.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.edo(19).divisions; // 19\n * Tuning.twelveTet().frequencyOf('A4'); // 440\n * ```\n */\nexport class Tuning {\n readonly #table: TuningTable;\n\n private constructor(table: TuningTable) {\n this.#table = table;\n }\n\n /**\n * Wrap a plain tuning table.\n *\n * The table is checked the way {@link Tuning.edo} checks its arguments, so a\n * deserialized or hand-written one cannot enter carrying a division count or\n * a reference frequency no frequency follows from.\n *\n * @param table The plain tuning table.\n * @returns The wrapped tuning.\n * @throws If the divisions are not a positive integer, or the reference step\n * or frequency is not a finite positive number.\n * @example\n * ```ts\n * import { TWELVE_TET } from '../../src/core/tuning/index.js';\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.of(TWELVE_TET).divisions; // 12\n * ```\n */\n static of(table: TuningTable): Tuning {\n // The tuning module's own constructor does the checking and returns a\n // fresh table, so a caller's object cannot become library state.\n return new Tuning(edo(table.divisions, table.refFreq, table.refStep));\n }\n\n /**\n * Build an equal temperament with `divisions` divisions of the octave.\n *\n * @param divisions Divisions of the octave (e.g. 19, 24, 31).\n * @param refFreq Reference frequency in Hz; 440 by default.\n * @param refStep Step index of the reference; 69 by default, the MIDI number\n * of A4.\n * @returns The tuning.\n * @throws If the divisions are not a positive integer, or the reference step\n * or frequency is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Math.round(Tuning.edo(19).centsOfSteps(1)); // 63\n * ```\n */\n static edo(divisions: number, refFreq?: number, refStep?: number): Tuning {\n return new Tuning(edo(divisions, refFreq, refStep));\n }\n\n /**\n * Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.\n *\n * @returns The tuning in which a step index is an ordinary MIDI number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().stepOf(440); // 69\n * ```\n */\n static twelveTet(): Tuning {\n return Tuning.of(TWELVE_TET);\n }\n\n /**\n * Rebuild a tuning from the plain table {@link Tuning.data} hands out.\n *\n * @param data The plain tuning table.\n * @returns The wrapped tuning.\n * @throws If the table describes no temperament; see {@link Tuning.of}.\n */\n static fromData(data: TuningTable): Tuning {\n return Tuning.of(data);\n }\n\n /**\n * Rebuild a tuning from its {@link Tuning.toJSON} output.\n *\n * @param data The serialized tuning table.\n * @returns The wrapped tuning.\n * @throws If the table describes no temperament; see {@link Tuning.of}.\n */\n static fromJSON(data: TuningTable): Tuning {\n return Tuning.of(data);\n }\n\n /**\n * Cents of a frequency ratio, e.g. 3:2 for the just perfect fifth.\n *\n * A ratio is a relation between two frequencies rather than a place in a\n * temperament, so this is the same figure under every tuning.\n *\n * @param numerator Ratio numerator.\n * @param denominator Ratio denominator.\n * @returns The interval in cents.\n * @throws If either side is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Math.round(Tuning.ratioToCents(3, 2)); // 702\n * ```\n */\n static ratioToCents(numerator: number, denominator: number): number {\n return ratioToCents(numerator, denominator);\n }\n\n /**\n * The frequency ratio an interval in cents spans, the inverse of\n * {@link Tuning.ratioToCents}. Multiply a frequency by it to move the pitch\n * by that many cents.\n *\n * @param cents The interval in cents.\n * @returns The frequency ratio.\n * @throws If `cents` is not finite.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.centsToRatio(0); // 1\n * ```\n */\n static centsToRatio(cents: number): number {\n return centsToRatio(cents);\n }\n\n /**\n * Interval in cents between two frequencies.\n *\n * @param a Lower/first frequency in Hz.\n * @param b Upper/second frequency in Hz.\n * @returns Cents from `a` to `b`, negative when `b` is lower.\n * @throws If either frequency is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Math.round(Tuning.centsBetweenFreq(440, 880)); // 1200\n * ```\n */\n static centsBetweenFreq(a: number, b: number): number {\n return centsBetweenFreq(a, b);\n }\n\n /**\n * Cents by which a five-limit just interval departs from its 12-TET\n * tempering; positive means the just interval is the wider one.\n *\n * @param semitoneClass Semitone class in [0, 12].\n * @returns The deviation in cents.\n * @throws If `semitoneClass` is not an integer in [0, 12].\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Math.round(Tuning.justDeviationCents(7)); // 2\n * ```\n */\n static justDeviationCents(semitoneClass: number): number {\n return justDeviationCents(semitoneClass);\n }\n\n /** Step index whose frequency is {@link Tuning.refFreq}. */\n get refStep(): number {\n return this.#table.refStep;\n }\n\n /** Frequency in Hz of {@link Tuning.refStep}. */\n get refFreq(): number {\n return this.#table.refFreq;\n }\n\n /** Equal divisions of the octave; 12 is the standard temperament. */\n get divisions(): number {\n return this.#table.divisions;\n }\n\n /** A copy of the underlying plain tuning table. */\n get data(): TuningTable {\n return this.toJSON();\n }\n\n /**\n * Frequency in Hz of a note.\n *\n * The note's MIDI number is read as a step index, which is how the tuning\n * module numbers steps: under twelve divisions the two coincide, and under\n * any other the step index keeps counting from the same reference.\n *\n * @param note A note name, a MIDI number, plain note data, or a `Note`; it\n * must carry an octave, since a bare pitch class has no frequency.\n * @param tuning A temperament to read the note under instead of this one.\n * @returns The frequency in Hz.\n * @throws If the value is not a note, has no octave, or the frequency falls\n * outside the range a number holds.\n * @example\n * ```ts\n * import { Note } from '@libraz/libcantus';\n * import { Tuning } from '@libraz/libcantus';\n * const tuning = Tuning.twelveTet();\n * tuning.frequencyOf('A4'); // 440\n * tuning.frequencyOf(69); // 440\n * tuning.frequencyOf(Note.parse('A4')); // 440\n * ```\n */\n frequencyOf(note: NoteLike, tuning: TuningTable = this.#table): number {\n return frequencyOf(noteToMidi(toNoteData(note)), tuning);\n }\n\n /**\n * Frequency in Hz of a step index.\n *\n * The step-numbered form of {@link Tuning.frequencyOf}, for the steps of a\n * temperament that no twelve-tone note names.\n *\n * @param step Step index; a MIDI number under twelve divisions.\n * @param tuning A temperament to read the step under instead of this one.\n * @returns The frequency in Hz.\n * @throws If the step is not finite, or the frequency falls outside the range\n * a number holds.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * const et19 = Tuning.edo(19);\n * et19.frequencyOfStep(69); // 440\n * et19.frequencyOfStep(70) > 440; // true\n * ```\n */\n frequencyOfStep(step: number, tuning: TuningTable = this.#table): number {\n return frequencyOf(step, tuning);\n }\n\n /**\n * Nearest step index to a frequency, the rounded inverse of\n * {@link Tuning.frequencyOfStep}.\n *\n * @param freq Frequency in Hz.\n * @param tuning A temperament to read the frequency under instead of this one.\n * @returns The nearest step index.\n * @throws If the frequency is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().nearestStep(442); // 69\n * ```\n */\n nearestStep(freq: number, tuning: TuningTable = this.#table): number {\n return nearestStep(freq, tuning);\n }\n\n /**\n * Step index of a frequency, unrounded.\n *\n * The exact inverse of {@link Tuning.frequencyOfStep}, keeping how far off\n * the pitch sits — the figure a tuner display or a pitch-bend amount is built\n * on, which {@link Tuning.nearestStep} rounds away.\n *\n * @param freq Frequency in Hz.\n * @param tuning A temperament to read the frequency under instead of this one.\n * @returns The fractional step index.\n * @throws If the frequency is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().stepOf(442) > 69; // true\n * ```\n */\n stepOf(freq: number, tuning: TuningTable = this.#table): number {\n return stepOf(freq, tuning);\n }\n\n /**\n * How far a frequency sits from its nearest step, in cents; positive means\n * sharp of the step, negative flat.\n *\n * @param freq Frequency in Hz.\n * @param tuning A temperament to read the frequency under instead of this one.\n * @returns The signed deviation in cents.\n * @throws If the frequency is not a finite positive number.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Math.round(Tuning.twelveTet().centsFromNearestStep(442)); // 8\n * ```\n */\n centsFromNearestStep(freq: number, tuning: TuningTable = this.#table): number {\n return centsFromNearestStep(freq, tuning);\n }\n\n /**\n * Cents spanned by a number of steps.\n *\n * @param steps Number of steps.\n * @param tuning A temperament to measure the steps in instead of this one.\n * @returns The cents.\n * @throws If the step count is not finite.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().centsOfSteps(1); // 100\n * ```\n */\n centsOfSteps(steps: number, tuning: TuningTable = this.#table): number {\n return centsOfSteps(steps, tuning);\n }\n\n /**\n * Steps spanned by an interval in cents, unrounded — the inverse of\n * {@link Tuning.centsOfSteps}.\n *\n * @param cents The interval in cents.\n * @param tuning A temperament to measure the interval in instead of this one.\n * @returns The fractional step count.\n * @throws If the cents are not finite.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().stepsOfCents(700); // 7\n * ```\n */\n stepsOfCents(cents: number, tuning: TuningTable = this.#table): number {\n return stepsOfCents(cents, tuning);\n }\n\n /**\n * Whether another tuning divides the octave the same way from the same\n * reference pitch.\n *\n * @param other The tuning to compare with.\n * @returns True when reference step, reference frequency, and divisions all\n * match.\n */\n equals(other: Tuning): boolean {\n // The other tuning is read through its public accessors rather than its\n // private field: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n return (\n this.#table.refStep === other.refStep &&\n this.#table.refFreq === other.refFreq &&\n this.#table.divisions === other.divisions\n );\n }\n\n /**\n * The plain tuning table, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(tuning)` from collapsing to `{}`.\n *\n * @returns A copy of the reference step, reference frequency, and divisions.\n */\n toJSON(): TuningTable {\n return {\n refStep: this.#table.refStep,\n refFreq: this.#table.refFreq,\n divisions: this.#table.divisions,\n };\n }\n\n /**\n * The temperament as a readable label, so a template literal or a log line\n * reads as the tuning.\n *\n * @returns The divisions and the reference pitch, e.g.\n * `'12-EDO (step 69 = 440 Hz)'`.\n * @example\n * ```ts\n * import { Tuning } from '@libraz/libcantus';\n * Tuning.twelveTet().toString(); // '12-EDO (step 69 = 440 Hz)'\n * ```\n */\n toString(): string {\n return `${this.#table.divisions}-EDO (step ${this.#table.refStep} = ${this.#table.refFreq} Hz)`;\n }\n}\n","import { InvalidInputError } from '../core/errors/index.js';\nimport type { Note as NoteData, NoteLike } from '../core/pitch/index.js';\nimport { toNoteData } from '../core/pitch/index.js';\nimport type { KeyScale } from '../core/types.js';\nimport { assertInteger, assertMidiPitch } from '../core/validation/index.js';\nimport {\n type VoiceIndependenceOptions,\n type VoiceIndependenceReport,\n voiceIndependence,\n} from '../theory/counterpoint/index.js';\nimport {\n checkPartWriting,\n checkSpecies,\n type PartWritingOptions,\n type PartWritingViolation,\n type Species,\n type SpeciesOptions,\n type SpelledVoicing,\n spellVoicing,\n} from '../theory/partwriting/index.js';\nimport {\n type EvaluateSafetyOptions,\n enumerateSafePitches,\n evaluateSafety,\n type SafetyQuery,\n type SafetyResult,\n type VoiceSnapshot,\n} from '../theory/safety/index.js';\nimport { type KeyLike, spelledKeyOf, toKeyScale } from '../theory/scale/index.js';\nimport { spellPitch } from '../theory/spelling/index.js';\nimport { type ChordLike, toChordData } from '../theory/symbol/index.js';\nimport {\n nextVoicing,\n SATB_RANGES,\n type StyledVoicingOptions,\n type VoiceRange,\n type VoicingOptions,\n voiceChord,\n voiceChordStyled,\n voiceLeadingCost,\n} from '../theory/voicing/index.js';\nimport { Note } from './note.js';\n\n/**\n * The plain form of a {@link Voicing}: the sounding MIDI pitches, ascending.\n *\n * The voicing functions all speak this array, so the class hands out exactly\n * what they take rather than a wrapper of its own.\n */\nexport type VoicingData = number[];\n\n/**\n * Options controlling {@link Voicing.independence}.\n *\n * Everything {@link voiceIndependence} accepts, plus the key its two lines are\n * spelled in.\n */\nexport type VoicingIndependenceOptions = VoiceIndependenceOptions & {\n /**\n * The key both lines are spelled in. Without it the pitches are read as MIDI\n * and spelled with sharps, the reading {@link toNoteData} takes of a bare\n * number; the spelling decides only whether two voices moving alike keep the\n * same interval and which intervals count as perfect consonances.\n */\n key?: KeyLike;\n};\n\n/**\n * The context a candidate pitch is judged in, with the voices of this voicing\n * standing as the other parts.\n *\n * The same query serves {@link Voicing.safetyOf} and\n * {@link Voicing.safePitches}: it is everything {@link SafetyQuery} carries\n * except the candidate and the other voices, which the voicing supplies, with\n * the chord and key taken in whichever form the caller holds them. It is\n * derived from the query itself, so a field the safety module adds arrives\n * here rather than being silently dropped on the way.\n */\nexport type VoicingSafetyQuery = Omit<\n SafetyQuery,\n 'candidatePitch' | 'otherVoices' | 'chord' | 'key'\n> & {\n /** The chord sounding under the candidate, or null when none is known. */\n chord: ChordLike | null;\n /** The prevailing key. */\n key: KeyLike;\n /**\n * The voicing these voices moved from, aligned voice for voice with this\n * one. It is what lets the parallel, hidden-parallel and voice-crossing\n * rules apply: without a previous pitch per voice there is no motion to read.\n */\n previous?: Voicing;\n};\n\n/**\n * Defensive copy of the pitches, each checked as a sounding pitch.\n *\n * The order is the caller's and is never sorted into place. A voicing is\n * ascending, but a voicing that is not is exactly what {@link checkPartWriting}\n * reports as voice crossing, and sorting here would silently repair the fault\n * the check exists to find.\n */\nfunction copyPitches(pitches: readonly number[]): number[] {\n if (pitches.length === 0) {\n throw new InvalidInputError('a voicing must contain at least one pitch');\n }\n // Every pitch is checked rather than copied blind: a voicing holding a NaN\n // leads, spells and scores as a voicing that looks real, and the failure\n // surfaces wherever the number is finally used. MIDI is the bound the safety\n // and voicing functions already hold their pitches to.\n return pitches.map((pitch, index) => assertMidiPitch(pitch, `pitches[${index}]`));\n}\n\n/**\n * Spell a line of pitches in a key.\n *\n * A bare number is spelled by the key rather than read as a sharp-spelled MIDI\n * note, so a cantus firmus given as pitches is written the way the voicing it\n * is judged against is: the rules that read letters — the augmented second, the\n * diminished fourth — cannot be applied to two lines spelled by different\n * rules.\n */\nfunction spellLine(line: readonly NoteLike[], key: KeyScale): NoteData[] {\n const { tonic } = spelledKeyOf(key);\n return line.map((value) =>\n typeof value === 'number' ? spellPitch(value, tonic, key) : toNoteData(value),\n );\n}\n\n/**\n * A line of pitches as notes: spelled by the key when one is known, and read as\n * sharp-spelled MIDI when none is — the reading {@link toNoteData} takes of a\n * bare number.\n */\nfunction lineOf(pitches: readonly number[], key: KeyScale | undefined): NoteData[] {\n return key === undefined ? pitches.map((pitch) => toNoteData(pitch)) : spellLine(pitches, key);\n}\n\n/**\n * An immutable voicing: the pitches one chord sounds at one moment, lowest\n * voice first (index 0 = the bass), the voice order {@link voiceChord} and\n * {@link SATB_RANGES} already use.\n *\n * The order given is kept rather than sorted: a voicing whose voices are out of\n * order is one whose voices cross, which is a fault {@link Voicing.checkTo}\n * reports rather than one the class quietly repairs.\n *\n * The voicing, part-writing, counterpoint and safety functions all take the\n * same array of voices; this holds it once and passes it to each of them. Two\n * of the methods read the pitches as a line rather than a chord —\n * {@link Voicing.species} and {@link Voicing.independence}, which judge a\n * written part slot by slot — and say so.\n *\n * The individual counterpoint predicates (`createsParallelPerfect`,\n * `createsVoiceCrossing`, and the rest) stay standalone functions: they judge\n * one pair of voices at one moment, which is a question about two pitches\n * rather than about a voicing. {@link Voicing.checkTo} is the bundled check.\n *\n * @category Class API\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * Voicing.satb('C').pitches.length; // 4\n * ```\n */\nexport class Voicing {\n readonly #pitches: readonly number[];\n\n /**\n * Wrap a set of sounding pitches.\n *\n * @param pitches MIDI pitches, lowest voice first; the array is copied,\n * never retained or mutated, and its order is kept. A doubled pitch is\n * kept too, since doubling is how a four-voice texture states a triad.\n * @throws If the array is empty, or a pitch is not an integer in 0..127.\n */\n constructor(pitches: readonly number[]) {\n this.#pitches = Object.freeze(copyPitches(pitches));\n }\n\n /**\n * Wrap a set of sounding pitches.\n *\n * @param pitches MIDI pitches, lowest voice first.\n * @returns The voicing.\n * @throws If the array is empty, or a pitch is not an integer in 0..127.\n */\n static of(pitches: readonly number[]): Voicing {\n return new Voicing(pitches);\n }\n\n /**\n * Realize a chord as an explicit tertian voicing in a chosen style — close,\n * drop-2, drop-3, shell or rootless — independent of the SATB range search\n * {@link Voicing.satb} runs.\n *\n * @param chord A chord symbol, plain chord data, or a {@link Chord}.\n * @param opts Styled voicing options; defaults to a close voicing at octave 4.\n * @returns The voicing, ascending.\n * @throws If the voicing would not fit inside MIDI 0..127 at the given\n * octave, or if the style leaves no voice sounding.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * Voicing.forChord('Dm7', { style: 'drop2' }).pitches.length; // 4\n * ```\n */\n static forChord(chord: ChordLike, opts?: StyledVoicingOptions): Voicing {\n return new Voicing(voiceChordStyled(toChordData(chord), opts));\n }\n\n /**\n * Realize a chord as one pitch per voice inside the voice ranges, defaulting\n * to the four {@link SATB_RANGES}.\n *\n * The bass takes the chord's slash bass when it has one, otherwise the root;\n * the result is compact, centred in its ranges, and free of voice crossing.\n *\n * @param chord A chord symbol, plain chord data, or a {@link Chord}.\n * @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.\n * @returns The voicing, ascending.\n * @throws If no voicing fits the given ranges.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * Voicing.satb('Cmaj7').range.min >= 40; // true — the bass stays in its compass\n * ```\n */\n static satb(chord: ChordLike, opts?: VoicingOptions): Voicing {\n return new Voicing(voiceChord(toChordData(chord), opts));\n }\n\n /**\n * Wrap a plain pitch array, as {@link Voicing.data} hands it out.\n *\n * @param data The MIDI pitches.\n * @returns The voicing.\n * @throws If the array is empty, or a pitch is not an integer in 0..127.\n */\n static fromData(data: readonly number[]): Voicing {\n return new Voicing(data);\n }\n\n /**\n * Rebuild a voicing from its {@link Voicing.toJSON} output.\n *\n * @param data The serialized pitches.\n * @returns The voicing.\n * @throws If the array is empty, or a pitch is not an integer in 0..127.\n */\n static fromJSON(data: readonly number[]): Voicing {\n return new Voicing(data);\n }\n\n /**\n * A copy of the default four-voice SATB ranges, ascending: the compass\n * {@link Voicing.satb} voices into and {@link Voicing.checkTo} judges a\n * four-voice exercise against.\n */\n static get satbRanges(): VoiceRange[] {\n return SATB_RANGES.map((range) => ({ min: range.min, max: range.max }));\n }\n\n /** A copy of the sounding MIDI pitches, lowest voice first. */\n get pitches(): number[] {\n return [...this.#pitches];\n }\n\n /**\n * The compass the voicing occupies: its lowest and highest sounding pitch.\n *\n * Read as a {@link VoiceRange} so it can be handed straight back as a range\n * for another voice. The pitches are searched rather than read off the ends,\n * so a voicing whose voices cross still reports the compass it sounds.\n */\n get range(): VoiceRange {\n return { min: Math.min(...this.#pitches), max: Math.max(...this.#pitches) };\n }\n\n /** A copy of the underlying plain pitch array. */\n get data(): VoicingData {\n return this.toJSON();\n }\n\n /**\n * Spell the voicing as notes with letters and octaves, so the rules that read\n * letters can be applied to it.\n *\n * Each pitch is named against the key, and against the chord sounding under\n * it when one is given, so a chord tone takes the letter its interval above\n * the root implies: the third of a D major chord in C major spells F#, not\n * Gb.\n *\n * @param key A key name, a plain key/scale, or a {@link Key}.\n * @param chord The chord sounding, supplying the enharmonic evidence; without\n * it the key alone decides.\n * @returns One spelled note per pitch, ascending.\n * @throws If the value names no key or no chord.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * Voicing.of([50, 57, 66, 69]).spell('C major', 'D').map((note) => note.name);\n * // ['D3', 'A3', 'F#4', 'A4']\n * ```\n */\n spell(key: KeyLike, chord?: ChordLike): Note[] {\n return this.#spelled(toKeyScale(key), chord).map((note) => new Note(note));\n }\n\n /**\n * Voice a chord to follow smoothly from this voicing.\n *\n * Candidates are enumerated within the ranges `opts` names, or — when neither\n * `voices` nor `ranges` is given — within a one-octave window around each of\n * these pitches, so the answer keeps this voicing's voice count. Each is\n * scored by structural quality, motion from here, and a large penalty per\n * counterpoint violation.\n *\n * @param chord A chord symbol, plain chord data, or a {@link Chord}.\n * @param opts Voicing options; when omitted, the ranges follow this voicing.\n * @returns The chosen voicing.\n * @throws If this voicing has fewer voices than the requested ranges, or no\n * voicing fits them.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * const tonic = Voicing.satb('C');\n * tonic.costTo(tonic.next('G7')) < 24; // true — the voices move economically\n * ```\n */\n next(chord: ChordLike, opts?: VoicingOptions): Voicing {\n return new Voicing(nextVoicing(this.pitches, toChordData(chord), opts));\n }\n\n /**\n * The voice-leading cost from this voicing to another: the summed absolute\n * semitone motion across voices, plus a penalty when the outer voices reach a\n * perfect fifth or octave by similar motion.\n *\n * @param other The voicing moved to.\n * @returns The cost, or `Infinity` when the two hold a different number of\n * voices and are therefore not comparable.\n */\n costTo(other: Voicing): number {\n return voiceLeadingCost(this.pitches, other.pitches);\n }\n\n /**\n * Check the motion from this voicing to another against the four-part rules,\n * and report every rule the pair breaks.\n *\n * Both voicings are spelled in `key` against the chord each realizes, then\n * graded as a two-chord exercise: voice crossing, spacing and range inside\n * each chord, and parallel and hidden perfects, overlap, cross relations,\n * augmented melodic intervals and unresolved tendency tones between them.\n *\n * @param other The voicing moved to.\n * @param chords The chord this voicing realizes and the chord `other` does.\n * @param key The key the exercise is written in; it names the leading tone.\n * @param opts Ranges and the upper-voice spacing limit; a four-voice pair is\n * judged against {@link SATB_RANGES} when no ranges are given.\n * @returns Every violation found, in musical order; an empty array for a\n * clean pair.\n * @throws If a value names no chord or key, or the ranges are malformed or\n * too few for the voices being checked.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * const from = Voicing.of([48, 55, 64, 72]);\n * const to = Voicing.of([50, 57, 65, 69]);\n * from.checkTo(to, ['C', 'Dm'], 'C major').map((found) => found.kind);\n * // ['parallelFifth']\n * ```\n */\n checkTo(\n other: Voicing,\n chords: readonly [ChordLike, ChordLike],\n key: KeyLike,\n opts?: PartWritingOptions,\n ): PartWritingViolation[] {\n const scale = toKeyScale(key);\n const from = toChordData(chords[0]);\n const to = toChordData(chords[1]);\n const voicings: SpelledVoicing[] = [\n spellVoicing(this.pitches, from, scale),\n spellVoicing(other.pitches, to, scale),\n ];\n return checkPartWriting(voicings, [from, to], scale, opts);\n }\n\n /**\n * Mark this voicing, read as a written counterpoint line, against a cantus\n * firmus in one of the five species.\n *\n * The pitches are the counterpoint, one per slot rather than one per voice:\n * this is the one place where the array is a line in time instead of a chord\n * in register. Both lines are spelled in `mode`, since half of what the\n * species rules forbid — the augmented second, the diminished fourth — is\n * invisible in a pitch.\n *\n * @param cantusFirmus The given voice, one note per measure, as note names,\n * plain notes, MIDI pitches, or {@link Note} instances.\n * @param species Which species the exercise is written in.\n * @param mode The mode the exercise is in.\n * @param opts Note lengths, and which side the counterpoint is written on;\n * the fifth species cannot be read without `durations`.\n * @returns Every violation found, in the order the exercise commits them.\n * @throws If the species is not one of the five, the cantus firmus is empty,\n * or the fifth species is given without durations.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * const counterpoint = Voicing.of([72, 69, 67, 71, 72]);\n * counterpoint.species(['C4', 'D4', 'E4', 'D4', 'C4'], 1, 'C major').length; // 0\n * ```\n */\n species(\n cantusFirmus: readonly NoteLike[],\n species: Species,\n mode: KeyLike,\n opts?: SpeciesOptions,\n ): PartWritingViolation[] {\n const scale = toKeyScale(mode);\n // Both lines are spelled by the same reading, so a cantus firmus given as\n // pitches is written the way this one is: the rules that read letters\n // cannot be applied to two lines spelled by different rules.\n return checkSpecies(\n spellLine(cantusFirmus, scale),\n spellLine(this.#pitches, scale),\n species,\n scale,\n opts,\n );\n }\n\n /**\n * Measure how independent this voicing, read as a line, is from another.\n *\n * As in {@link Voicing.species} the pitches are slots in time, one per entry,\n * aligned with `other` slot for slot. Nothing here is a verdict: parallel\n * thirds report as parallel motion with a small separation, a pedal point as\n * oblique motion.\n *\n * @param other The line measured against this one, of the same length.\n * @param opts Attack flags, the treatment of the fourth, and the key both\n * lines are spelled in.\n * @returns The motion breakdown, rhythmic complementarity, separation,\n * crossings, and the longest perfect-consonance run.\n * @throws If the two lines differ in length, or an attack array is supplied\n * at another length than the lines.\n */\n independence(other: Voicing, opts?: VoicingIndependenceOptions): VoiceIndependenceReport {\n // `key` is this class's own addition: it spells the two lines and has no\n // meaning below. Forwarding the bag whole would hand a `KeyLike` to a\n // delegate that today discards it and tomorrow may read a `key` of its\n // own, so it is taken out here rather than relied on being ignored.\n const { key, ...rest } = opts ?? {};\n const scale = key === undefined ? undefined : toKeyScale(key);\n return voiceIndependence(lineOf(this.#pitches, scale), lineOf(other.pitches, scale), rest);\n }\n\n /**\n * Judge a candidate pitch placed against these voices.\n *\n * Every pitch of this voicing stands as another part sounding under the\n * candidate, so the vertical rules read them all; the motion rules need\n * `query.previous`, which gives each of those voices the pitch it came from.\n *\n * @param pitch The candidate MIDI pitch.\n * @param query The candidate's harmonic and voice-leading context.\n * @param opts Set `suggestions: false` to skip the search for safe\n * replacements when the verdict is not safe.\n * @returns The verdict, reason bitmask, and optional resolution guidance.\n * @throws If the profile is unknown, a value names no chord or key, or a\n * pitch is not a MIDI pitch in 0..127.\n * @example\n * ```ts\n * import { NoteSafety } from '@libraz/libcantus';\n * import { Voicing } from '@libraz/libcantus';\n * const sounding = Voicing.of([48, 55, 64]);\n * sounding.safetyOf(72, { profile: 'pop', chord: 'C', key: 'C major', strongBeat: true })\n * .safety === NoteSafety.Safe; // true\n * ```\n */\n safetyOf(pitch: number, query: VoicingSafetyQuery, opts?: EvaluateSafetyOptions): SafetyResult {\n return evaluateSafety({ ...this.#context(query), candidatePitch: pitch }, opts);\n }\n\n /**\n * Enumerate the pitches placeable against these voices in a range, chord\n * tones first, each group descending.\n *\n * @param query The harmonic and voice-leading context, as\n * {@link Voicing.safetyOf} reads it.\n * @param pitchLow Lowest MIDI pitch to consider (inclusive).\n * @param pitchHigh Highest MIDI pitch to consider (inclusive).\n * @returns The placeable pitches — those not judged dissonant.\n * @throws If the profile is unknown, a value names no chord or key, or a\n * bound is not a MIDI pitch in 0..127 or is reversed.\n */\n safePitches(query: VoicingSafetyQuery, pitchLow: number, pitchHigh: number): number[] {\n return enumerateSafePitches(this.#context(query), pitchLow, pitchHigh);\n }\n\n /**\n * The n-th inversion of the voicing: the lowest voice taken up an octave, n\n * times over.\n *\n * A negative `n` takes the highest voice down an octave as many times, and\n * `invert(0)` is the voicing itself. Unlike {@link Chord.invert}, which\n * rotates a chord's interval template, this moves real voices: inverting a\n * four-voice texture four times leaves the same chord an octave higher. The\n * result is ascending, since the voice that moved is no longer where it was.\n *\n * @param n How many voices to move, upward when positive.\n * @returns The inverted voicing, ascending.\n * @throws If `n` is not an integer in [-128, 128], or a voice would leave\n * MIDI 0..127.\n * @example\n * ```ts\n * import { Voicing } from '@libraz/libcantus';\n * Voicing.of([60, 64, 67]).invert(1).pitches; // [64, 67, 72]\n * ```\n */\n invert(n: number): Voicing {\n assertInteger(n, 'voicing inversion', -128, 128);\n // Each move is made in turn rather than in one arithmetic step: the voice\n // that is lowest after a move is not always the next one up, so which voice\n // moves second depends on where the first one landed.\n const pitches = [...this.#pitches].sort((a, b) => a - b);\n for (let moved = 0; moved < Math.abs(n); moved += 1) {\n const index = n > 0 ? 0 : pitches.length - 1;\n const pitch = (pitches[index] as number) + (n > 0 ? 12 : -12);\n pitches.splice(index, 1);\n pitches.push(assertMidiPitch(pitch, 'inverted voice'));\n pitches.sort((a, b) => a - b);\n }\n return new Voicing(pitches);\n }\n\n /**\n * Whether another voicing sounds the same pitches in the same order.\n *\n * A doubling counts: two voices on the same pitch are two voices, and a\n * three-voice texture is not the four-voice one that doubles its root.\n *\n * @param other The voicing to compare.\n * @returns True when the pitches match one for one.\n */\n equals(other: Voicing): boolean {\n // The other voicing is read through its public accessor rather than its\n // private field: a bundler that emits two copies of this class — as a\n // CommonJS build without shared chunks does for the root and /model\n // entries — would otherwise throw on the brand check.\n const theirs = other.pitches;\n return (\n this.#pitches.length === theirs.length &&\n this.#pitches.every((pitch, index) => pitch === theirs[index])\n );\n }\n\n /**\n * The plain pitch array, for JSON serialization.\n *\n * Private class fields do not serialize, so an explicit `toJSON` keeps\n * `JSON.stringify(voicing)` from collapsing to `{}`.\n *\n * @returns A copy of the sounding MIDI pitches, ascending.\n */\n toJSON(): VoicingData {\n return [...this.#pitches];\n }\n\n /**\n * The pitches separated by spaces, so a template literal or a log line reads\n * as the voicing.\n *\n * @returns The pitches ascending, e.g. `'60 64 67'`.\n */\n toString(): string {\n return this.#pitches.join(' ');\n }\n\n /** Spell the voicing, by the key alone or against a chord as well. */\n #spelled(key: KeyScale, chord: ChordLike | undefined): NoteData[] {\n return chord === undefined\n ? spellLine(this.#pitches, key)\n : spellVoicing(this.pitches, toChordData(chord), key);\n }\n\n /** The safety context these voices supply, minus the candidate pitch. */\n #context(query: VoicingSafetyQuery): Omit<SafetyQuery, 'candidatePitch'> {\n const previous = query.previous?.pitches;\n const otherVoices: VoiceSnapshot[] = this.#pitches.map((pitch, index) => {\n const prevPitch = previous?.[index];\n return prevPitch === undefined ? { pitch } : { pitch, prevPitch };\n });\n return {\n profile: query.profile,\n chord: query.chord === null ? null : toChordData(query.chord),\n key: toKeyScale(query.key),\n otherVoices,\n strongBeat: query.strongBeat,\n prevPitch: query.prevPitch,\n vocalLow: query.vocalLow,\n vocalHigh: query.vocalHigh,\n };\n }\n}\n"]}