@libraz/libcantus 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -261
- package/README_ja.md +115 -0
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +213 -456
- package/dist/analyze/index.d.ts +213 -456
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/chunk-ELCTZUJJ.js +379 -0
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +103 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +96 -345
- package/dist/core/index.d.ts +96 -345
- package/dist/core/index.js +104 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +437 -1095
- package/dist/generate/index.d.ts +437 -1095
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +358 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +407 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4685 -243
- package/dist/model/index.d.ts +4685 -243
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +513 -173
- package/dist/theory/index.d.ts +513 -173
- package/dist/theory/index.js +118 -24
- package/package.json +16 -11
- package/dist/chunk-36N73KZ3.cjs +0 -246
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs +0 -3077
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs +0 -498
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js +0 -3077
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs +0 -679
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js +0 -479
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js +0 -1449
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs +0 -30
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs +0 -479
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js +0 -173
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-HKGYCQ34.js +0 -53
- package/dist/chunk-HKGYCQ34.js.map +0 -1
- package/dist/chunk-I24MV65U.js +0 -106
- package/dist/chunk-I24MV65U.js.map +0 -1
- package/dist/chunk-KMHOICIY.js +0 -679
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs +0 -1117
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NKIBQ6PT.js +0 -30
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs +0 -106
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs +0 -1449
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js +0 -327
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js +0 -845
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs +0 -327
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs +0 -845
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js +0 -1117
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-RUMU3UA6.cjs +0 -53
- package/dist/chunk-RUMU3UA6.cjs.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-XEBG3G53.js +0 -498
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js +0 -246
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs +0 -173
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- package/dist/function-BtiZb9i9.d.ts +0 -208
- package/dist/function-DNugAOrt.d.cts +0 -208
- package/dist/index-6VR8-kit.d.ts +0 -143
- package/dist/index-70p168VM.d.ts +0 -192
- package/dist/index-Be74mcon.d.cts +0 -152
- package/dist/index-BtXT6UQ0.d.cts +0 -192
- package/dist/index-C5UBbQ4G.d.ts +0 -152
- package/dist/index-C83mZI1P.d.ts +0 -242
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-OG65ocOr.d.cts +0 -272
- package/dist/index-OG65ocOr.d.ts +0 -272
- package/dist/index-tJhkmQgE.d.cts +0 -143
- package/dist/index-y_ak9_Hi.d.cts +0 -242
- package/dist/styled-DIRkk9Uz.d.ts +0 -506
- package/dist/styled-DJbkJQfj.d.cts +0 -506
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theory/scale/masks.ts","../src/theory/scale/key.ts","../src/theory/scale/coerce.ts","../src/theory/scale/degrees.ts","../src/theory/scale/signature.ts","../src/theory/scale/relations.ts","../src/theory/scale/system.ts","../src/theory/chord/spec.ts","../src/theory/chord/index.ts","../src/theory/spelling/index.ts","../src/theory/counterpoint/internal.ts","../src/theory/counterpoint/independence.ts","../src/theory/counterpoint/index.ts","../src/theory/safety/index.ts","../src/theory/symbol/index.ts"],"sourcesContent":["import { InvalidInputError } from '../../core/errors/index.js';\nimport { assertInteger } from '../../core/validation/index.js';\n\n/**\n * Build a 12-bit mode mask from a list of semitone offsets above the root.\n *\n * Bit 0 (the root) is always set, enforcing the `KeyScale` invariant that the\n * root is a scale tone even when the offset list omits 0.\n *\n * @category Scales\n */\nexport function maskFromOffsets(offsets: readonly number[]): number {\n let mask = 1;\n for (const [index, offset] of offsets.entries()) {\n assertInteger(offset, `offsets[${index}]`);\n mask |= 1 << (((offset % 12) + 12) % 12);\n }\n return mask;\n}\n\n/**\n * Mode mask for the major (Ionian) scale: offsets {0, 2, 4, 5, 7, 9, 11}.\n *\n * @category Scales\n */\nexport const MAJOR_MASK = 0b101010110101;\n\n/**\n * Mode mask for the natural minor (Aeolian) scale: offsets {0, 2, 3, 5, 7, 8, 10}.\n *\n * @category Scales\n */\nexport const NATURAL_MINOR_MASK = 0b010110101101;\n\n/**\n * Harmonic minor: natural minor with a raised seventh — offsets {0,2,3,5,7,8,11}.\n *\n * @category Scales\n */\nexport const HARMONIC_MINOR_MASK = maskFromOffsets([0, 2, 3, 5, 7, 8, 11]);\n\n/**\n * Ascending melodic minor: offsets {0,2,3,5,7,9,11}.\n *\n * @category Scales\n */\nexport const MELODIC_MINOR_MASK = maskFromOffsets([0, 2, 3, 5, 7, 9, 11]);\n\n/**\n * Dorian mode: offsets {0,2,3,5,7,9,10}.\n *\n * @category Scales\n */\nexport const DORIAN_MASK = maskFromOffsets([0, 2, 3, 5, 7, 9, 10]);\n\n/**\n * Phrygian mode: offsets {0,1,3,5,7,8,10}.\n *\n * @category Scales\n */\nexport const PHRYGIAN_MASK = maskFromOffsets([0, 1, 3, 5, 7, 8, 10]);\n\n/**\n * Lydian mode: offsets {0,2,4,6,7,9,11}.\n *\n * @category Scales\n */\nexport const LYDIAN_MASK = maskFromOffsets([0, 2, 4, 6, 7, 9, 11]);\n\n/**\n * Mixolydian mode: offsets {0,2,4,5,7,9,10}.\n *\n * @category Scales\n */\nexport const MIXOLYDIAN_MASK = maskFromOffsets([0, 2, 4, 5, 7, 9, 10]);\n\n/**\n * Locrian mode: offsets {0,1,3,5,6,8,10}.\n *\n * @category Scales\n */\nexport const LOCRIAN_MASK = maskFromOffsets([0, 1, 3, 5, 6, 8, 10]);\n\n/**\n * Major pentatonic: offsets {0,2,4,7,9}.\n *\n * @category Scales\n */\nexport const MAJOR_PENTATONIC_MASK = maskFromOffsets([0, 2, 4, 7, 9]);\n\n/**\n * Minor pentatonic: offsets {0,3,5,7,10}.\n *\n * @category Scales\n */\nexport const MINOR_PENTATONIC_MASK = maskFromOffsets([0, 3, 5, 7, 10]);\n\n/**\n * Hexatonic blues scale: minor pentatonic plus the flat-fifth — {0,3,5,6,7,10}.\n *\n * @category Scales\n */\nexport const BLUES_MASK = maskFromOffsets([0, 3, 5, 6, 7, 10]);\n\n/**\n * Whole-tone scale: offsets {0,2,4,6,8,10}.\n *\n * @category Scales\n */\nexport const WHOLE_TONE_MASK = maskFromOffsets([0, 2, 4, 6, 8, 10]);\n\n/**\n * Octatonic (half-step first): offsets {0,1,3,4,6,7,9,10}.\n *\n * @category Scales\n */\nexport const OCTATONIC_HALF_WHOLE_MASK = maskFromOffsets([0, 1, 3, 4, 6, 7, 9, 10]);\n\n/**\n * Octatonic (whole-step first): offsets {0,2,3,5,6,8,9,11}.\n *\n * @category Scales\n */\nexport const OCTATONIC_WHOLE_HALF_MASK = maskFromOffsets([0, 2, 3, 5, 6, 8, 9, 11]);\n\n/**\n * Chromatic scale: all twelve pitch classes.\n *\n * @category Scales\n */\nexport const CHROMATIC_MASK = 0b111111111111;\n\n/**\n * Lydian dominant (melodic minor's fourth mode): offsets {0,2,4,6,7,9,10}.\n *\n * The scale over an unaltered dominant with a raised eleventh.\n *\n * @category Scales\n */\nexport const LYDIAN_DOMINANT_MASK = maskFromOffsets([0, 2, 4, 6, 7, 9, 10]);\n\n/**\n * Mixolydian b13, the aeolian dominant (melodic minor's fifth mode): offsets\n * {0,2,4,5,7,8,10}.\n *\n * The scale over a dominant with a flat thirteenth.\n *\n * @category Scales\n */\nexport const MIXOLYDIAN_B13_MASK = maskFromOffsets([0, 2, 4, 5, 7, 8, 10]);\n\n/**\n * Locrian natural 2 (melodic minor's sixth mode): offsets {0,2,3,5,6,8,10}.\n *\n * The scale over a half-diminished chord used as a minor ii.\n *\n * @category Scales\n */\nexport const LOCRIAN_NATURAL2_MASK = maskFromOffsets([0, 2, 3, 5, 6, 8, 10]);\n\n/**\n * The altered scale, or superlocrian (melodic minor's seventh mode): offsets\n * {0,1,3,4,6,8,10}.\n *\n * The scale over an altered dominant: every tension raised or lowered.\n *\n * @category Scales\n */\nexport const ALTERED_MASK = maskFromOffsets([0, 1, 3, 4, 6, 8, 10]);\n\n/**\n * Phrygian dominant (harmonic minor's fifth mode): offsets {0,1,4,5,7,8,10}.\n *\n * The scale over the dominant of a minor key, and the characteristic sound of\n * the flamenco and maqam repertoires.\n *\n * @category Scales\n */\nexport const PHRYGIAN_DOMINANT_MASK = maskFromOffsets([0, 1, 4, 5, 7, 8, 10]);\n\n/**\n * Miyako-bushi (都節), the hemitonic pentatonic of Japanese urban art music —\n * shamisen and koto song: offsets {0,1,5,7,8}.\n *\n * Two miyako-bushi tetrachords a fourth apart, in the terms of Koizumi's\n * tetrachord theory: between the two nuclear tones of each tetrachord sits a\n * note a semitone above the lower one.\n *\n * The mask fixes the pitch classes and nothing else: the repertoire's\n * intonation is not twelve-tone equal temperament, and the scale is a pair of\n * tetrachords around nuclear tones rather than a ladder of degrees above a\n * tonic. Cents and non-12 temperaments are the tuning module's business.\n *\n * @category Scales\n */\nexport const MIYAKO_BUSHI_MASK = maskFromOffsets([0, 1, 5, 7, 8]);\n\n/**\n * Ritsu (律), the anhemitonic pentatonic of Japanese court music and Buddhist\n * chant: offsets {0,2,5,7,9}.\n *\n * Two ritsu tetrachords a fourth apart: the middle note sits a whole tone above\n * the lower nuclear tone. The result is the same five pitch classes as the\n * major pentatonic a fourth below, which is why the mask alone cannot say which\n * tradition is meant.\n *\n * @category Scales\n */\nexport const RITSU_MASK = maskFromOffsets([0, 2, 5, 7, 9]);\n\n/**\n * Ryūkyū (琉球), the pentatonic of Okinawan music: offsets {0,4,5,7,11}.\n *\n * Two ryūkyū tetrachords a fourth apart: the middle note sits a major third\n * above the lower nuclear tone, which is what puts the two semitone steps under\n * the fourth and the octave.\n *\n * @category Scales\n */\nexport const RYUKYU_MASK = maskFromOffsets([0, 4, 5, 7, 11]);\n\n/**\n * The heptatonic set with two augmented seconds: offsets {0,1,4,5,7,8,11}.\n *\n * Maqam Hijazkar in Arabic practice, thaat Bhairav in Hindustani practice, and\n * the double harmonic major scale in English-language scale lists — one\n * pitch-class set that three traditions arrived at separately and treat\n * differently.\n *\n * @category Scales\n */\nexport const DOUBLE_HARMONIC_MASK = maskFromOffsets([0, 1, 4, 5, 7, 8, 11]);\n\n/**\n * Thaat Todi of Hindustani classical music — S r g M' P d N: offsets\n * {0,1,3,6,7,8,11}.\n *\n * @category Scales\n */\nexport const TODI_MASK = maskFromOffsets([0, 1, 3, 6, 7, 8, 11]);\n\n/**\n * Thaat Marwa of Hindustani classical music — S r G M' P D N: offsets\n * {0,1,4,6,7,9,11}.\n *\n * The thaat carries the fifth; raga Marwa itself leaves it out, which is the\n * kind of distinction a pitch-class mask cannot make.\n *\n * @category Scales\n */\nexport const MARWA_MASK = maskFromOffsets([0, 1, 4, 6, 7, 9, 11]);\n\n/**\n * Thaat Purvi of Hindustani classical music — S r G M' P d N: offsets\n * {0,1,4,6,7,8,11}.\n *\n * @category Scales\n */\nexport const PURVI_MASK = maskFromOffsets([0, 1, 4, 6, 7, 8, 11]);\n\n/**\n * Named scale masks addressable by {@link scaleByName}.\n *\n * These are the scales of Western common practice and of jazz, the vocabulary\n * chord-scale theory is stated in. Scales belonging to traditions that chord\n * function does not organise live in {@link WORLD_SCALES}, which\n * {@link scaleByName} resolves just as readily; {@link scaleSystemOf} says\n * which kind a name is.\n *\n * @category Scales\n */\nexport const NAMED_SCALES = Object.freeze({\n major: MAJOR_MASK,\n ionian: MAJOR_MASK,\n naturalMinor: NATURAL_MINOR_MASK,\n aeolian: NATURAL_MINOR_MASK,\n harmonicMinor: HARMONIC_MINOR_MASK,\n melodicMinor: MELODIC_MINOR_MASK,\n dorian: DORIAN_MASK,\n phrygian: PHRYGIAN_MASK,\n lydian: LYDIAN_MASK,\n mixolydian: MIXOLYDIAN_MASK,\n locrian: LOCRIAN_MASK,\n lydianDominant: LYDIAN_DOMINANT_MASK,\n mixolydianB13: MIXOLYDIAN_B13_MASK,\n locrianNatural2: LOCRIAN_NATURAL2_MASK,\n altered: ALTERED_MASK,\n phrygianDominant: PHRYGIAN_DOMINANT_MASK,\n majorPentatonic: MAJOR_PENTATONIC_MASK,\n minorPentatonic: MINOR_PENTATONIC_MASK,\n blues: BLUES_MASK,\n wholeTone: WHOLE_TONE_MASK,\n octatonicHalfWhole: OCTATONIC_HALF_WHOLE_MASK,\n octatonicWholeHalf: OCTATONIC_WHOLE_HALF_MASK,\n chromatic: CHROMATIC_MASK,\n});\n\n/**\n * The name of a built-in scale, as accepted by {@link scaleByName},\n * {@link avoidNotes}, and {@link availableTensions}.\n *\n * @category Scales\n */\nexport type ScaleName = keyof typeof NAMED_SCALES;\n\n/**\n * Scale masks from traditions outside Western common practice, addressable by\n * {@link scaleByName} exactly as {@link NAMED_SCALES} is.\n *\n * They are kept apart from {@link NAMED_SCALES} because chord-scale theory is a\n * Western practice: a raga offered as the scale over an augmented major seventh\n * would read as an answer while being a category error. Nothing else separates\n * them — a `KeyScale` built from one of these masks behaves like any other.\n *\n * Three limits are worth stating plainly rather than leaving a caller to\n * discover:\n *\n * - A twelve-bit mask can only hold what twelve-tone equal temperament can\n * express. The maqamat that turn on half-flat degrees — Rast, Bayati, Saba,\n * Sikah — are therefore absent rather than approximated, and the entries that\n * are here are not tuned the way their traditions tune them. Cents, arbitrary\n * equal divisions and just ratios live in the tuning module.\n * - A thaat is the pitch material a raga draws on, not the raga: ascent and\n * descent, the notes a phrase leans on, and the ornaments belong to the raga\n * and have no place in a mask.\n * - Several of these sets are the same pitch classes as a Western mode. The\n * name is what carries the tradition, so pass the name, not the mask,\n * wherever the tradition matters ({@link scaleSystemOf} reads both).\n *\n * The second limit is the general case: an entry is the pitch material a\n * tradition names, never the modal system built on it. A maqam is assembled\n * from ajnas that are transposed and exchanged as a phrase moves, and Koizumi's\n * tetrachord theory describes the Japanese scales by the nuclear tones framing\n * each tetrachord, so `miyakoBushi` and `minyo` differ by tetrachord type — a\n * difference their pitch classes record without explaining. Reach for these\n * entries when a pitch set is what is wanted.\n *\n * @category Scales\n */\nexport const WORLD_SCALES = Object.freeze({\n /** Miyako-bushi (都節), the hemitonic pentatonic of Japanese art music. */\n miyakoBushi: MIYAKO_BUSHI_MASK,\n /** Ritsu (律), the anhemitonic pentatonic of Japanese court music and chant. */\n ritsu: RITSU_MASK,\n /**\n * Min'yō (民謡), the pentatonic of Japanese folk song, called inaka-bushi\n * (田舎節) opposite miyako-bushi. The same five pitch classes as the minor\n * pentatonic.\n */\n minyo: MINOR_PENTATONIC_MASK,\n /** Ryūkyū (琉球), the pentatonic of Okinawan music. */\n ryukyu: RYUKYU_MASK,\n /** Maqam Ajam of Arabic practice; the same pitch classes as the major scale. */\n ajam: MAJOR_MASK,\n /**\n * Maqam Nahawand of Arabic practice; the same pitch classes as the natural\n * minor, though practice raises the seventh degree at a cadence.\n */\n nahawand: NATURAL_MINOR_MASK,\n /** Maqam Kurd of Arabic practice; the same pitch classes as the phrygian mode. */\n kurd: PHRYGIAN_MASK,\n /**\n * Maqam Hijaz of Arabic practice; the same pitch classes as the phrygian\n * dominant. Played intonation stretches the augmented second beyond its\n * tempered size, which is the sound the mask cannot carry.\n */\n hijaz: PHRYGIAN_DOMINANT_MASK,\n /** Maqam Hijazkar of Arabic practice: Hijaz over Hijaz, with two augmented seconds. */\n hijazkar: DOUBLE_HARMONIC_MASK,\n /** Thaat Bilaval of Hindustani classical music; the same pitch classes as the major scale. */\n bilaval: MAJOR_MASK,\n /** Thaat Khamaj; the same pitch classes as the mixolydian mode. */\n khamaj: MIXOLYDIAN_MASK,\n /** Thaat Kafi; the same pitch classes as the dorian mode. */\n kafi: DORIAN_MASK,\n /** Thaat Asavari; the same pitch classes as the natural minor. */\n asavari: NATURAL_MINOR_MASK,\n /** Thaat Bhairavi; the same pitch classes as the phrygian mode. */\n bhairavi: PHRYGIAN_MASK,\n /** Thaat Bhairav, with komal Re and komal Dha against a natural third and seventh. */\n bhairav: DOUBLE_HARMONIC_MASK,\n /** Thaat Kalyan; the same pitch classes as the lydian mode. */\n kalyan: LYDIAN_MASK,\n /** Thaat Marwa, with komal Re and tivra Ma. */\n marwa: MARWA_MASK,\n /** Thaat Purvi, Marwa's komal Dha counterpart. */\n purvi: PURVI_MASK,\n /** Thaat Todi, with komal Re, komal Ga, komal Dha and tivra Ma. */\n todi: TODI_MASK,\n});\n\n/**\n * The name of a scale in {@link WORLD_SCALES}.\n *\n * @category Scales\n */\nexport type WorldScaleName = keyof typeof WORLD_SCALES;\n\n/**\n * Alternative names that resolve to a canonical scale name.\n *\n * A scale carrying several names gets one canonical entry and its other names\n * here, so that two spellings of one tradition's scale cannot drift into two\n * entries with two masks. Transliterations vary, and one pitch-class set is\n * often named differently in different repertoires.\n *\n * @category Scales\n */\nexport const SCALE_ALIASES = Object.freeze({\n /** Inaka-bushi (田舎節), the name the in/yō pairing gives the min'yō scale. */\n inakaBushi: 'minyo',\n /** The Okinawan scale, the usual English name for the ryūkyū scale. */\n okinawan: 'ryukyu',\n /** Turkish spelling of Hijaz. */\n hicaz: 'hijaz',\n /** The English-language scale-list name for the Hijazkar/Bhairav set. */\n doubleHarmonic: 'hijazkar',\n /** Transliteration variant of Bilaval. */\n bilawal: 'bilaval',\n /** Transliteration variant of Marwa. */\n marva: 'marwa',\n /** Transliteration variant of Purvi. */\n poorvi: 'purvi',\n} as const satisfies Readonly<Record<string, ScaleName | WorldScaleName>>);\n\n/**\n * An alternative scale name that {@link resolveScaleName} maps to a canonical one.\n *\n * @category Scales\n */\nexport type ScaleAliasName = keyof typeof SCALE_ALIASES;\n\n/**\n * A scale name that completes to the built-in names but still accepts any\n * string, so a caller with a name from configuration is not forced to cast.\n *\n * @category Scales\n */\nexport type ScaleNameInput = ScaleName | (string & {});\n\n/**\n * The canonical name a scale name stands for, or undefined when it names no\n * built-in scale.\n *\n * A canonical name resolves to itself; an entry of {@link SCALE_ALIASES}\n * resolves to the name it is an alias of. The lookup goes through\n * `Object.hasOwn` for the same reason {@link namedScaleMask} does.\n *\n * @param name The scale name or alias.\n * @returns The canonical name, or undefined for an unknown one.\n * @example\n * ```ts\n * import { resolveScaleName } from '@libraz/libcantus';\n * resolveScaleName('okinawan'); // 'ryukyu'\n * resolveScaleName('dorian'); // 'dorian'\n * ```\n * @category Scales\n */\nexport function resolveScaleName(name: ScaleNameInput): ScaleName | WorldScaleName | undefined {\n if (Object.hasOwn(NAMED_SCALES, name)) {\n return name as ScaleName;\n }\n if (Object.hasOwn(WORLD_SCALES, name)) {\n return name as WorldScaleName;\n }\n return Object.hasOwn(SCALE_ALIASES, name) ? SCALE_ALIASES[name as ScaleAliasName] : undefined;\n}\n\n/**\n * The mask of a named scale, or undefined when the name is not one.\n *\n * Names of {@link NAMED_SCALES}, of {@link WORLD_SCALES} and of\n * {@link SCALE_ALIASES} all answer here, so a caller never has to know which\n * register a scale is kept in.\n *\n * Looking the name up through this helper is what keeps `'constructor'` or\n * `'toString'` from resolving to something inherited from `Object.prototype`\n * and producing a scale whose mask is a function.\n *\n * @param name The scale name or alias.\n * @returns The 12-bit mask, or undefined for an unknown name.\n * @category Scales\n */\nexport function namedScaleMask(name: ScaleNameInput): number | undefined {\n const canonical = resolveScaleName(name);\n if (canonical === undefined) {\n return undefined;\n }\n return Object.hasOwn(NAMED_SCALES, canonical)\n ? NAMED_SCALES[canonical as ScaleName]\n : WORLD_SCALES[canonical as WorldScaleName];\n}\n\n/**\n * The mask of a named scale, rejecting an unknown name.\n *\n * Every public entry point that takes a scale name funnels through here, so a\n * typo is reported the same way everywhere instead of being answered with an\n * empty result that also means \"this scale has none\".\n *\n * @param name The scale name.\n * @param label What the name is, for the error message.\n * @returns The 12-bit mask.\n * @throws If the name is not a built-in scale.\n * @category Scales\n */\nexport function requireScaleMask(name: ScaleNameInput, label = 'scale'): number {\n const mask = namedScaleMask(name);\n if (mask === undefined) {\n throw new InvalidInputError(`Unknown ${label}: ${String(name)}`);\n }\n return mask;\n}\n","import { pitchClassOf } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport type { ScaleNameInput } from './masks.js';\nimport { MAJOR_MASK, NATURAL_MINOR_MASK, requireScaleMask } from './masks.js';\n\n/**\n * Build a `KeyScale` for a major key on the given root pitch class.\n *\n * @example\n * ```ts\n * import { majorKey } from '@libraz/libcantus';\n * const cMajor = majorKey(0); // C major: { rootPc: 0, modeMask12: MAJOR_MASK }\n * ```\n *\n * @category Scales\n */\nexport function majorKey(rootPc: number): KeyScale {\n return { rootPc: pitchClassOf(rootPc), modeMask12: MAJOR_MASK };\n}\n\n/**\n * Build a `KeyScale` for a natural-minor key on the given root pitch class.\n *\n * @example\n * ```ts\n * import { minorKey } from '@libraz/libcantus';\n * const aMinor = minorKey(9); // A natural minor: { rootPc: 9, modeMask12: NATURAL_MINOR_MASK }\n * ```\n *\n * @category Scales\n */\nexport function minorKey(rootPc: number): KeyScale {\n return { rootPc: pitchClassOf(rootPc), modeMask12: NATURAL_MINOR_MASK };\n}\n\n/**\n * Build a `KeyScale` from a named scale (see {@link NAMED_SCALES}).\n *\n * @param name The scale name, e.g. `'dorian'` or `'harmonicMinor'`.\n * @param rootPc The root pitch class.\n * @returns The key/scale.\n * @throws If the name is not a known scale.\n *\n * @example\n * ```ts\n * import { scaleByName } from '@libraz/libcantus';\n * const dDorian = scaleByName('dorian', 2); // D Dorian, rootPc 2\n * ```\n *\n * @category Scales\n */\nexport function scaleByName(name: ScaleNameInput, rootPc: number): KeyScale {\n return { rootPc: pitchClassOf(rootPc), modeMask12: requireScaleMask(name) };\n}\n","import { InvalidInputError } from '../../core/errors/index.js';\nimport { noteToPitchClass, parseKeyName, pitchClassOf } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { assertInteger } from '../../core/validation/index.js';\nimport { majorKey, minorKey } from './key.js';\nimport { CHROMATIC_MASK } from './masks.js';\n\n/**\n * Anything that names a key: a key name, a plain {@link KeyScale}, or a value\n * that serializes to one such as the `Key` class.\n *\n * @category Scales\n */\nexport type KeyLike =\n | string\n | KeyScale\n | {\n /** The key data this value stands for, carrying the scale it denotes. */\n toJSON(): { scale: KeyScale };\n };\n\n/** Validate a plain key/scale and return it in the canonical shape. */\nfunction normalizedKeyScale(scale: KeyScale): KeyScale {\n assertInteger(scale.modeMask12, 'key.modeMask12', 1, CHROMATIC_MASK);\n if ((scale.modeMask12 & 1) === 0) {\n // Every scale contains its own root, and the rest of the library reads\n // bit 0 as given. A mask without it names no key and would degrade into\n // wrong answers far from here rather than failing at the boundary.\n throw new InvalidInputError(\n `key.modeMask12 must include its root; received ${scale.modeMask12}`,\n );\n }\n assertInteger(scale.rootPc, 'key.rootPc');\n return { rootPc: pitchClassOf(scale.rootPc), modeMask12: scale.modeMask12 };\n}\n\n/**\n * Resolve any key-shaped value to a plain {@link KeyScale}.\n *\n * The counterpart of {@link toSpelledInterval} for keys: an entry point takes\n * whatever form the caller has — the name a key field holds, the scale the\n * theory module returns, or a `Key` instance — and gets one shape back. An\n * instance is accepted through its `toJSON` method rather than by its type, so\n * the layers below the model can read a class without importing it.\n *\n * A name is read by {@link parseKeyName} and nothing else, so every entry point\n * that takes a key accepts exactly the names a key field already accepts, in\n * every notation system it already reads. The mode word decides the scale: a\n * major name gives the major scale, a minor name the natural minor. The\n * harmonic and melodic forms have no name of their own, so a caller who needs\n * one builds it with {@link scaleByName} and passes the scale.\n *\n * @param value A key name, a plain key/scale, or a value whose `toJSON`\n * returns data carrying one.\n * @returns The validated key/scale, with its root reduced to a pitch class.\n * @throws If the value names no key, or its mode mask is out of range or\n * excludes its own root.\n * @example\n * ```ts\n * import { toKeyScale } from '@libraz/libcantus';\n * toKeyScale('C major').rootPc; // 0\n * toKeyScale('A minor').rootPc; // 9\n * ```\n * @category Scales\n */\nexport function toKeyScale(value: KeyLike): KeyScale {\n if (typeof value === 'string') {\n const name = parseKeyName(value);\n const rootPc = noteToPitchClass(name.tonic);\n return name.mode === 'major' ? majorKey(rootPc) : minorKey(rootPc);\n }\n if (typeof value === 'object' && value !== null) {\n const data =\n 'toJSON' in value && typeof value.toJSON === 'function' ? value.toJSON().scale : value;\n if (typeof data === 'object' && data !== null) {\n return normalizedKeyScale(data as KeyScale);\n }\n }\n throw new InvalidInputError(`key must be a key name or a key/scale; received ${typeof value}`);\n}\n","import { pitchClassOf as pitchClass } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { clampToMidi } from '../../core/validation/index.js';\nimport { type KeyLike, toKeyScale } from './coerce.js';\n\n/** Scale offset of a pitch relative to the key root, in [0, 11]. */\nfunction scaleOffset(pitch: number, key: KeyScale): number {\n return (pitchClass(pitch) - pitchClass(key.rootPc) + 12) % 12;\n}\n\n/** Whether a pitch belongs to an already-resolved scale. */\nfunction inScale(pitch: number, scale: KeyScale): boolean {\n return ((scale.modeMask12 >> scaleOffset(pitch, scale)) & 1) === 1;\n}\n\n/**\n * Test whether a pitch belongs to the scale.\n *\n * @param pitch MIDI pitch or bare pitch class.\n * @param key The key/scale to test against. This predicate reads the mask as\n * given rather than taking a key name: the spelling search runs it over\n * masks that name no key, which resolving a name would refuse.\n * @returns True if the pitch class is a member of the scale.\n *\n * @example\n * ```ts\n * import { majorKey, isScaleTone } from '@libraz/libcantus';\n * const key = majorKey(0); // C major\n * isScaleTone(64, key); // true — E is in C major\n * isScaleTone(61, key); // false — C# is not\n * ```\n *\n * @category Scales\n */\nexport function isScaleTone(pitch: number, key: KeyScale): boolean {\n return inScale(pitch, key);\n}\n\n/**\n * Find the nearest MIDI pitch whose pitch class is in the scale.\n *\n * The search expands symmetrically outward from `pitch`. On a tie (equal\n * distance above and below) the lower pitch is returned.\n *\n * @param pitch MIDI pitch to snap.\n * @param key The key/scale to snap to, as a key name, a key/scale, or a `Key`.\n * @returns The nearest in-scale MIDI pitch.\n *\n * @category Scales\n */\nexport function nearestScaleTone(pitch: number, key: KeyLike): number {\n const scale = toKeyScale(key);\n const base = clampToMidi(Math.round(pitch), 'pitch');\n for (let distance = 0; distance < 12; distance += 1) {\n const lower = base - distance;\n if (lower >= 0 && inScale(lower, scale)) {\n return lower;\n }\n const higher = base + distance;\n if (higher <= 127 && inScale(higher, scale)) {\n return higher;\n }\n }\n return base;\n}\n\n/** The key's scale offsets above its root, ascending within one octave. */\nfunction scaleOffsets(key: KeyScale): number[] {\n const offsets: number[] = [];\n for (let n = 0; n < 12; n += 1) {\n if (((key.modeMask12 >> n) & 1) === 1) {\n offsets.push(n);\n }\n }\n return offsets;\n}\n\n/**\n * Where a pitch sits on the key's ladder of scale tones.\n *\n * `rung` counts scale tones from the tonic and continues across octaves, so\n * adding to it moves by scale degrees; it is negative below the tonic of octave\n * 0. `offset` is how many semitones the pitch sounds above that rung, which is 0\n * for a scale tone and positive for one between two of them.\n */\nexport type ScaleLadderPosition = {\n rung: number;\n offset: number;\n};\n\n/**\n * Locate a pitch on the key's ladder of scale tones.\n *\n * Anything counting in scale degrees has to put a pitch on this ladder and back\n * again afterwards. Keeping the chromatic offset apart from the rung is what\n * lets a note between two scale tones survive the arithmetic as the note it was:\n * without it a shift by any number of degrees — including none — would first\n * flatten every chromatic pitch onto the scale.\n *\n * In C major, C# sits on the tonic's rung with an offset of one semitone.\n *\n * Layer-internal: the generators count degrees with it, and callers reach the\n * same arithmetic through the transforms that use it.\n *\n * @param pitch MIDI pitch.\n * @param key The key/scale to count along.\n * @returns The rung the pitch sits on and its semitone offset above it.\n */\nexport function scaleLadderPosition(pitch: number, key: KeyScale): ScaleLadderPosition {\n const offsets = scaleOffsets(key);\n const size = offsets.length;\n if (size === 0) {\n return { rung: 0, offset: 0 };\n }\n const relative = pitch - pitchClass(key.rootPc);\n const octave = Math.floor(relative / 12);\n const within = relative - octave * 12;\n let degree = 0;\n for (let n = 0; n < size; n += 1) {\n if ((offsets[n] ?? 0) <= within) {\n degree = n;\n }\n }\n return { rung: octave * size + degree, offset: within - (offsets[degree] ?? 0) };\n}\n\n/**\n * The pitch a rung of the key's ladder of scale tones stands for.\n *\n * The inverse of {@link scaleLadderPosition}'s `rung`: adding the position's\n * `offset` back to the result returns the pitch it came from.\n *\n * Layer-internal, like {@link scaleLadderPosition}.\n *\n * @param rung Scale tones counted from the tonic, negative below it.\n * @param key The key/scale to count along.\n * @returns The MIDI pitch of that scale tone.\n */\nexport function scaleLadderPitch(rung: number, key: KeyScale): number {\n const offsets = scaleOffsets(key);\n const size = offsets.length;\n if (size === 0) {\n return pitchClass(key.rootPc);\n }\n const octave = Math.floor(rung / size);\n const degree = rung - octave * size;\n return pitchClass(key.rootPc) + octave * 12 + (offsets[degree] ?? 0);\n}\n\n/**\n * Shift a pitch by scale degrees along the key's ladder of scale tones.\n *\n * A pitch between two scale tones keeps its distance above the one below it, so\n * a chromatic passing note is still a chromatic passing note after the shift,\n * and a shift of no degrees returns the pitch untouched: a step up the C-major\n * ladder answers C# with D#, not with D.\n *\n * Layer-internal. It is what `transposeDiatonic` and a tonal imitation both\n * count along, so the two answer the same shift the same way; a caller reaches\n * it through either of them.\n *\n * @param pitch MIDI pitch to shift.\n * @param degrees Scale degrees to move by; negative moves down.\n * @param key The key/scale to count along.\n * @returns The shifted pitch.\n */\nexport function shiftByScaleDegrees(pitch: number, degrees: number, key: KeyScale): number {\n const steps = Math.trunc(degrees);\n if (steps === 0) {\n return pitch;\n }\n const { rung, offset } = scaleLadderPosition(pitch, key);\n return scaleLadderPitch(rung + steps, key) + offset;\n}\n\n/**\n * Get the scale degree of a pitch, counted from 1: the tonic is degree 1.\n *\n * A pitch outside the scale answers -1 rather than 0, so a caller testing the\n * result for a degree cannot mistake the \"not in the scale\" answer for the\n * tonic.\n *\n * @param pitch MIDI pitch or bare pitch class.\n * @param key The key/scale to measure against, as a key name, a key/scale, or a\n * `Key`.\n * @returns The 1-based degree, or -1 if the pitch is not in the scale.\n *\n * @category Scales\n */\nexport function pitchToScaleDegree(pitch: number, key: KeyLike): number {\n const scale = toKeyScale(key);\n const offset = scaleOffset(pitch, scale);\n if (((scale.modeMask12 >> offset) & 1) === 0) {\n return -1;\n }\n let degree = 1;\n for (let n = 0; n < offset; n += 1) {\n if (((scale.modeMask12 >> n) & 1) === 1) {\n degree += 1;\n }\n }\n return degree;\n}\n\n/**\n * List the pitch classes of a key's scale.\n *\n * @param key The key/scale to enumerate. The two enumerators here take a plain\n * key/scale rather than a key name: they read the mask as given, including\n * the tonic-less masks the spelling search works through, which no key name\n * describes.\n * @returns The member pitch classes, sorted ascending in [0, 11].\n *\n * @category Scales\n */\nexport function diatonicPitchClasses(key: KeyScale): number[] {\n const scale = key;\n const root = pitchClass(scale.rootPc);\n const pcs: number[] = [];\n for (let n = 0; n < 12; n += 1) {\n if (((scale.modeMask12 >> n) & 1) === 1) {\n pcs.push((root + n) % 12);\n }\n }\n return pcs.sort((a, b) => a - b);\n}\n\n/**\n * List the pitch classes of a key's scale in ascending scale-degree order.\n *\n * The first entry is the root (degree 1); degrees follow the mask bits in\n * offset order rather than sorted pitch-class order.\n *\n * @param key The key/scale to enumerate, read as given for the reason\n * {@link diatonicPitchClasses} gives.\n * @returns The member pitch classes ordered by scale degree.\n *\n * @category Scales\n */\nexport function scaleTonesInDegreeOrder(key: KeyScale): number[] {\n const scale = key;\n const root = pitchClass(scale.rootPc);\n const pcs: number[] = [];\n for (let n = 0; n < 12; n += 1) {\n if (((scale.modeMask12 >> n) & 1) === 1) {\n pcs.push((root + n) % 12);\n }\n }\n return pcs;\n}\n","import type { Note, NoteLike } from '../../core/pitch/index.js';\nimport {\n diatonicLetterOf,\n pitchClassOf as mod12,\n naturalPitchClassOf,\n toNoteData,\n} from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { assertInteger, assertOneOf } from '../../core/validation/index.js';\nimport { majorKey, minorKey } from './key.js';\nimport { HARMONIC_MINOR_MASK, MAJOR_MASK, MELODIC_MINOR_MASK } from './masks.js';\n\n/**\n * Which of the two modes a key signature is read in.\n *\n * A signature names two keys — three sharps is both A major and F# minor — so\n * every entry point that turns a signature into a key, or that names the mode\n * of a key relation, selects between them with this.\n *\n * @example\n * ```ts\n * import type { KeyMode } from '@libraz/libcantus';\n * const mode: KeyMode = 'minor';\n * ```\n * @category Scales\n */\nexport type KeyMode = 'major' | 'minor';\n\n/** Position of each natural letter on the circle of fifths: F=-1, C=0, ... B=5. */\nconst LETTER_FIFTHS = [0, 2, 4, -1, 1, 3, 5] as const;\n\n/** The letters of the circle of fifths from F upward, as letter numbers. */\nconst FIFTHS_LETTERS = [3, 0, 4, 1, 5, 2, 6] as const;\n\n/** How many fifths a sharp or a flat on the tonic moves its signature. */\nconst FIFTHS_PER_ALTERATION = 7;\n\n/** Signature offset of the natural-minor mode: three fifths below its parallel major. */\nconst MINOR_MODE_OFFSET = -3;\n\n/** Widest signature this module builds a key for, including theoretical ones. */\nconst MAX_FIFTHS = 12;\n\n/** Rotate a 12-bit mode mask down by `semitones`, wrapping at the octave. */\nfunction rotateMask(mask: number, semitones: number): number {\n const steps = ((semitones % 12) + 12) % 12;\n return ((mask >> steps) | (mask << (12 - steps))) & 0b111111111111;\n}\n\n/**\n * Signature offset of each diatonic mode, keyed by its mode mask.\n *\n * The offset is how many fifths the mode's signature sits from the major key on\n * the same tonic, which is exactly the mode's own position in the circle: D\n * dorian is two fifths flatter than D major, and so carries no accidentals.\n */\nconst DIATONIC_MODE_OFFSETS = new Map<number, number>([\n [rotateMask(MAJOR_MASK, 5), 1], // lydian\n [MAJOR_MASK, 0], // ionian / major\n [rotateMask(MAJOR_MASK, 7), -1], // mixolydian\n [rotateMask(MAJOR_MASK, 2), -2], // dorian\n [rotateMask(MAJOR_MASK, 9), MINOR_MODE_OFFSET], // aeolian / natural minor\n [rotateMask(MAJOR_MASK, 4), -4], // phrygian\n [rotateMask(MAJOR_MASK, 11), -5], // locrian\n]);\n\n/** Whether a mode mask has a minor third and no major third — it leans flat. */\nfunction hasMinorThird(modeMask12: number): boolean {\n return ((modeMask12 >> 3) & 1) === 1 && ((modeMask12 >> 4) & 1) === 0;\n}\n\n/**\n * How many fifths a key's signature sits from the major key on the same tonic.\n *\n * A diatonic mode has its own answer. Anything else — harmonic and melodic\n * minor, and every scale that is not heptatonic — takes the signature of its\n * parallel major or minor instead, since its remaining tones are written as\n * accidentals rather than in the signature.\n */\nfunction modeOffset(key: KeyScale): number {\n const diatonic = DIATONIC_MODE_OFFSETS.get(key.modeMask12);\n if (diatonic !== undefined) {\n return diatonic;\n }\n return hasMinorThird(key.modeMask12) ? MINOR_MODE_OFFSET : 0;\n}\n\n/**\n * Whether a key is one that is actually written with a key signature.\n *\n * Each of the seven diatonic modes has a signature of its own, and the harmonic\n * and melodic minor are the minor key with a degree or two raised: G# harmonic\n * minor is written with G# minor's five sharps and an F## in front of the note,\n * which is why it is spelled G# and not Ab. Every other scale only borrows the\n * signature of its parallel major or minor as an approximation, so nothing\n * about how it is written follows from that signature — and how such a scale is\n * spelled has to be decided from the scale itself.\n *\n * @param key The key/scale.\n * @returns True when the signature is the key's own rather than a stand-in.\n */\nexport function isSignatureKey(key: KeyScale): boolean {\n return (\n DIATONIC_MODE_OFFSETS.has(key.modeMask12) ||\n key.modeMask12 === HARMONIC_MINOR_MASK ||\n key.modeMask12 === MELODIC_MINOR_MASK\n );\n}\n\n/**\n * The key signature of a key, as a signed count of sharps (positive) or flats\n * (negative) — the `fifths` value a notation format such as MusicXML writes.\n *\n * The tonic's letter and accidental place the key on the circle of fifths, and\n * the mode moves it from there: lydian +1, major 0, mixolydian -1, dorian -2,\n * natural minor -3, phrygian -4, locrian -5.\n *\n * A scale that is not a diatonic mode has no signature of its own, so it takes\n * the signature of its parallel major or minor: a scale with a minor third and\n * no major third counts as minor (-3), everything else as major (0). This is\n * why A harmonic minor reports 0 — its G# is written as an accidental, not in\n * the signature — and why A melodic minor reports 0 as well.\n *\n * The result is not clamped: a theoretical key such as Fb major reports -8.\n * Clamp to ±7 if the consuming format cannot express more.\n *\n * @param tonic The spelled tonic, as a note name, note data, or a `Note`; its\n * accidental is worth seven fifths.\n * @param key The key/scale. This one stays a plain key/scale: it reads any\n * 12-bit mask, including the tonic-less ones a key name cannot describe.\n * @returns The signed number of sharps (positive) or flats (negative).\n * @example\n * ```ts\n * import { keySignatureFifths, majorKey, minorKey, parseNote } from '@libraz/libcantus';\n * keySignatureFifths(parseNote('Bb'), majorKey(10)); // -2\n * keySignatureFifths('A', minorKey(9)); // 0\n * ```\n * @category Scales\n */\nexport function keySignatureFifths(tonic: NoteLike, key: KeyScale): number {\n const note = toNoteData(tonic);\n const scale = key;\n const letter = diatonicLetterOf(note.letter);\n assertInteger(note.alter, 'tonic.alter');\n assertInteger(scale.modeMask12, 'key.modeMask12', 1, 0b111111111111);\n return (LETTER_FIFTHS[letter] ?? 0) + FIFTHS_PER_ALTERATION * note.alter + modeOffset(scale);\n}\n\n/**\n * The key a signature denotes: the inverse of {@link keySignatureFifths} for\n * the two modes a signature is conventionally read in.\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 is\n * written, including the theoretical keys past ±7 (nine sharps is B# 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 spelled tonic and the matching key/scale.\n * @throws If `fifths` is not an integer in [-12, 12], or `mode` is not one of\n * `'major'` or `'minor'`.\n * @example\n * ```ts\n * import { keyFromFifths, formatNote } from '@libraz/libcantus';\n * formatNote(keyFromFifths(-2).tonic); // 'Bb'\n * formatNote(keyFromFifths(-2, 'minor').tonic); // 'G'\n * ```\n * @category Scales\n */\nexport function keyFromFifths(\n fifths: number,\n mode: KeyMode = 'major',\n): { tonic: Note; key: KeyScale } {\n assertInteger(fifths, 'fifths', -MAX_FIFTHS, MAX_FIFTHS);\n const which = assertOneOf(mode, ['major', 'minor'], 'mode');\n // The circle of fifths starts at F, so the major tonic of `fifths` sharps is\n // one step along it; a minor key is the relative minor, three fifths further.\n const index = fifths + 1 + (which === 'minor' ? -MINOR_MODE_OFFSET : 0);\n const letter = FIFTHS_LETTERS[((index % 7) + 7) % 7] ?? 0;\n const alter = Math.floor(index / 7);\n const rootPc = mod12(naturalPitchClassOf(letter) + alter);\n return {\n tonic: { letter, alter },\n key: which === 'minor' ? minorKey(rootPc) : majorKey(rootPc),\n };\n}\n","/**\n * How keys stand to one another: the relative, parallel, dominant and\n * subdominant keys, the enharmonic respelling of a key, and the relation that\n * holds between two given keys.\n *\n * Every relation but {@link parallelKeyOf} is computed in fifths space, on top\n * of {@link keySignatureFifths} and {@link keyFromFifths}. Stepping around the\n * circle of fifths and reading the tonic back off it is what keeps the spelling\n * right: the relative of Db major comes out as Bb minor, not A# minor.\n *\n * A relation always returns a plain major or minor key, because those are the\n * only keys these relations name; {@link enharmonicKeyOf} is the exception that\n * proves it, since a respelling is the same key rather than another one and so\n * keeps the mask it was handed. A key that is not a plain major or minor —\n * a church mode, harmonic minor, a pentatonic, an octatonic — is read through\n * the mode its third names, and the fifths are counted from where its tonic\n * stands on the circle rather than from the key's own signature: a mode's\n * signature carries its own offset (D dorian is two fifths flatter than D\n * major), which {@link keyFromFifths} does not know how to give back.\n * {@link parallelKeyOf} travels no fifths at all, so it keeps the tonic\n * spelling it was given.\n */\n\nimport type { Note, NoteLike } from '../../core/pitch/index.js';\nimport {\n diatonicLetterOf,\n noteToPitchClass,\n pitchClassOf,\n toNoteData,\n} from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { assertInteger } from '../../core/validation/index.js';\nimport { spellScale } from '../spelling/index.js';\nimport { type KeyLike, toKeyScale } from './coerce.js';\nimport { majorKey, minorKey } from './key.js';\nimport { CHROMATIC_MASK } from './masks.js';\nimport type { KeyMode } from './signature.js';\nimport { isSignatureKey, keyFromFifths, keySignatureFifths } from './signature.js';\n\n/** How many fifths apart the two spellings of one sounding key sit. */\nconst ENHARMONIC_FIFTHS = 12;\n\n/** Widest signature a key is actually written with. */\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 fifths a minor key's tonic stands above its signature: A minor is +3. */\nconst MINOR_TONIC_FIFTHS = 3;\n\n/**\n * The flattest and sharpest points on the circle a tonic is ever written at.\n *\n * A key is written with at most seven accidentals, which puts its tonic between\n * Cb — the tonic of the seven-flat major key — and A#, the tonic of the\n * seven-sharp minor one. Every pitch class is named once or twice inside that\n * span, and those names are the only ones a key is spelled on: F# and Gb are\n * both there, E# and Fb are not.\n */\nconst FLATTEST_TONIC_FIFTHS = -MAX_CONVENTIONAL_FIFTHS;\nconst SHARPEST_TONIC_FIFTHS = MAX_CONVENTIONAL_FIFTHS + MINOR_TONIC_FIFTHS;\n\n/** Whether a mode mask has a minor third and no major third — it leans flat. */\nfunction isMinorMode(modeMask12: number): boolean {\n return ((modeMask12 >> 3) & 1) === 1 && ((modeMask12 >> 4) & 1) === 0;\n}\n\n/** Validate a key's mode mask, naming it the way the caller sees it. */\nfunction assertModeMask(key: KeyScale, name = 'key'): number {\n return assertInteger(key.modeMask12, `${name}.modeMask12`, 1, CHROMATIC_MASK);\n}\n\n/**\n * Which of the two modes a key leans to.\n *\n * This is the same test the key-signature module applies, so a relation and a\n * signature never disagree about a key's mode.\n */\nfunction modeOf(key: KeyScale): KeyMode {\n return isMinorMode(assertModeMask(key)) ? 'minor' : 'major';\n}\n\n/** The other of the two modes a signature is read in. */\nfunction oppositeMode(mode: KeyMode): KeyMode {\n return mode === 'major' ? 'minor' : 'major';\n}\n\n/**\n * Whether two keys name the same key: same root pitch class, same mode.\n *\n * Both keys are read the way every relation here reads them — through the mode\n * their third names — rather than by comparing masks. A relation answers with a\n * plain major or minor key, so a key that arrives as A harmonic minor has to be\n * met on the same terms as the A minor it is a form of; matching masks would\n * make the relation hold in one direction and not in the other.\n */\nfunction soundsLike(a: KeyScale, b: KeyScale): boolean {\n return (\n pitchClassOf(a.rootPc) === pitchClassOf(b.rootPc) &&\n isMinorMode(a.modeMask12) === isMinorMode(b.modeMask12)\n );\n}\n\n/** Whether two tonics are written alike, octave aside. */\nfunction spelledLike(a: Note, b: Note): boolean {\n return diatonicLetterOf(a.letter) === diatonicLetterOf(b.letter) && a.alter === b.alter;\n}\n\n/**\n * A key paired with the spelled tonic that anchors its letter names.\n *\n * The tonic carries no octave: a key is rooted on a pitch class, and its\n * spelling is what decides whether the key is written Db major or C# major.\n *\n * @example\n * ```ts\n * import type { SpelledKey } from '@libraz/libcantus';\n * import { majorKey, parseNote } from '@libraz/libcantus';\n * const dbMajor: SpelledKey = { tonic: parseNote('Db'), key: majorKey(1) };\n * ```\n * @category Scales\n */\nexport type SpelledKey = { tonic: Note; key: KeyScale };\n\n/**\n * How one key stands to another, as reported by {@link keyRelationBetween} and\n * tagged onto every entry of {@link relatedKeysOf}.\n *\n * @example\n * ```ts\n * import type { KeyRelation } from '@libraz/libcantus';\n * const relation: KeyRelation = 'relative'; // C major -> A minor\n * ```\n * @category Scales\n */\nexport type KeyRelation =\n | 'same'\n | 'enharmonic'\n | 'relative'\n | 'parallel'\n | 'dominant'\n | 'subdominant'\n | 'relativeOfDominant'\n | 'relativeOfSubdominant';\n\n/**\n * What a candidate tonic costs the scale it is asked to spell: the double\n * accidentals it forces, the accidentals it needs altogether, and how far round\n * the circle of fifths the key it names is written.\n */\ntype TonicCost = { doubles: number; accidentals: number; fifths: number };\n\n/** How the scale reads when spelled from `tonic`. */\nfunction tonicCost(tonic: Note, key: KeyScale): TonicCost {\n let doubles = 0;\n let accidentals = 0;\n for (const note of spellScale(tonic, key)) {\n const alter = Math.abs(note.alter);\n accidentals += alter;\n if (alter >= DOUBLE_ACCIDENTAL) {\n doubles += 1;\n }\n }\n return { doubles, accidentals, fifths: Math.abs(keySignatureFifths(tonic, key)) };\n}\n\n/**\n * Whether the first tonic spells the key better than the second; ties keep the\n * first, which is the flatter one because the scan runs from the flat end.\n *\n * A key that is written with a signature is spelled on the tonic that signature\n * is written on — pitch class 8 in minor is G# with five sharps, not Ab with\n * seven, and the F## its harmonic form writes is an accidental over that\n * signature rather than a reason to respell the key. A scale that only borrows\n * a signature has nothing to go on but how it reads, so there the double\n * accidentals it forces come first and the borrowed signature only breaks ties.\n */\nfunction spellsBetter(a: TonicCost, b: TonicCost, signatureKey: boolean): boolean {\n const order: (keyof TonicCost)[] = signatureKey\n ? ['fifths', 'doubles', 'accidentals']\n : ['doubles', 'accidentals', 'fifths'];\n for (const term of order) {\n if (a[term] !== b[term]) {\n return a[term] < b[term];\n }\n }\n return false;\n}\n\n/**\n * Spell a key's tonic the conventional way: the letter and accidental the key\n * is written with, paired with the key exactly as given.\n *\n * The candidates are the tonics keys are actually written on — everything from\n * Cb to A#, which names each pitch class once or twice (C, but Db and C#) — and\n * which of them names the key depends on whether the key has a signature of its\n * own. A major or minor key, a church mode, and the harmonic and melodic minor\n * are spelled on the tonic their signature is written on: pitch class 1 in\n * major is Db, five flats, rather than C#, seven sharps; pitch class 8 in minor\n * is G#, five sharps, rather than Ab, seven flats; and G# harmonic minor writes\n * its raised seventh F## as an accidental over that signature rather than\n * respelling the key. Where two signatures are equally long — pitch class 6 in\n * major is F# at +6 and Gb at -6 — the flat side is taken.\n *\n * Every other scale only borrows the signature of its parallel major or minor,\n * so nothing follows from it and the scale is spelled on the tonic it reads\n * best from: the fewest double accidentals first, then the fewest accidentals\n * altogether, and only then the shorter signature. That is why pitch class 1\n * altered is C# — C# D E F G A B — rather than Db, which would have to write\n * four double flats to say the same thing, and why pitch class 8 minor\n * pentatonic is G# B C# D# F# rather than Ab B Db Eb Gb, which reads its third\n * as an augmented second.\n *\n * The mask's third picks the mode the signature is measured in — a minor third\n * and no major third reads as minor, anything else as major — and the key is\n * returned unchanged, so a scale keeps its own mask and only borrows a tonic\n * spelling. A caller that has a tonic spelling of its own always keeps it: this\n * only answers for a key that arrives as a bare pitch class.\n *\n * Every 12-bit mask and every integer root is answered — a root outside [0, 11]\n * is reduced first, and a mask with no third at all counts as major.\n *\n * The key is paired with the answer exactly as it was handed in, root and mask\n * untouched, so this takes a plain key/scale rather than the wider forms the\n * relations below accept: a coerced key could not be given back verbatim.\n *\n * @param key The key/scale to spell.\n * @returns The conventional tonic spelling, paired with `key` verbatim.\n * @example\n * ```ts\n * import { formatNote, majorKey, scaleByName, spelledKeyOf } from '@libraz/libcantus';\n * formatNote(spelledKeyOf(majorKey(1)).tonic); // 'Db'\n * formatNote(spelledKeyOf(scaleByName('harmonicMinor', 8)).tonic); // 'G#'\n * formatNote(spelledKeyOf(scaleByName('altered', 1)).tonic); // 'C#'\n * ```\n * @category Scales\n */\nexport function spelledKeyOf(key: KeyScale): SpelledKey {\n const scale = key;\n const rootPc = pitchClassOf(scale.rootPc);\n const signatureKey = isSignatureKey(scale);\n // Every pitch class is named somewhere in the span, so the starting tonic here\n // only stands in until the scan makes its first match.\n let tonic: Note = keyFromFifths(0).tonic;\n let best: TonicCost | undefined;\n // Scanning from the flat end and keeping only a strictly better candidate\n // settles an even tie on the flat side: pitch class 6 in major is Gb, and\n // pitch class 3 in minor is Eb.\n for (let fifths = FLATTEST_TONIC_FIFTHS; fifths <= SHARPEST_TONIC_FIFTHS; fifths += 1) {\n const candidate = keyFromFifths(fifths).tonic;\n if (noteToPitchClass(candidate) !== rootPc) {\n continue;\n }\n const cost = tonicCost(candidate, scale);\n if (best === undefined || spellsBetter(cost, best, signatureKey)) {\n tonic = candidate;\n best = cost;\n }\n }\n return { tonic, key };\n}\n\n/**\n * The relative key: the same key signature read in the other mode.\n *\n * C major and A minor share a signature, and so are each other's relative; the\n * relation is its own inverse for a plain major or minor key. Because the new\n * tonic is read off the circle of fifths rather than transposed by semitones,\n * the relative of Db major is Bb minor and not its enharmonic A# minor.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask\n * decides which mode the relative is in.\n * @returns The relative key and the tonic spelling it is written with.\n * @throws If the tonic or the mask is malformed, or if the key's signature\n * falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { formatNote, majorKey, parseNote, relativeKeyOf } from '@libraz/libcantus';\n * formatNote(relativeKeyOf(parseNote('C'), majorKey(0)).tonic); // 'A'\n * formatNote(relativeKeyOf('Db', majorKey(1)).tonic); // 'Bb'\n * ```\n * @category Scales\n */\nexport function relativeKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey {\n const scale = toKeyScale(key);\n return keyFromFifths(keySignatureFifths(tonic, scale), oppositeMode(modeOf(scale)));\n}\n\n/**\n * The parallel key: the other mode on the same tonic.\n *\n * The tonic keeps the spelling it was given — the parallel minor of C major is\n * C minor, never B# minor — which is why this is the one relation here that\n * does not travel the circle of fifths.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`; its letter and alteration are kept.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask\n * decides which mode the parallel is in.\n * @returns The parallel key on the same tonic.\n * @throws If the tonic or the mask is malformed.\n * @example\n * ```ts\n * import { formatNote, majorKey, parallelKeyOf, parseNote } from '@libraz/libcantus';\n * const parallel = parallelKeyOf(parseNote('C'), majorKey(0));\n * formatNote(parallel.tonic); // 'C'\n * ```\n * @category Scales\n */\nexport function parallelKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey {\n const note = toNoteData(tonic);\n const mode = oppositeMode(modeOf(toKeyScale(key)));\n // The spelled tonic decides the root, so the returned key and its tonic agree\n // even when the caller's `rootPc` was left on the other mode's root.\n const rootPc = noteToPitchClass(note);\n return {\n tonic: { letter: diatonicLetterOf(note.letter), alter: note.alter },\n key: mode === 'minor' ? minorKey(rootPc) : majorKey(rootPc),\n };\n}\n\n/** Where a spelled tonic itself stands on the circle of fifths: C at 0, F# at +6. */\nfunction tonicPosition(tonic: Note): number {\n return keySignatureFifths(tonic, majorKey(noteToPitchClass(tonic)));\n}\n\n/**\n * The signature the tonic's own plain major or minor key is written with.\n *\n * This, and not the key's signature, is what a relation that travels the circle\n * steps from. The two agree for a plain major or minor key and part company\n * everywhere else: G mixolydian has the signature of C major, so stepping from\n * that signature and reading the tonic back with {@link keyFromFifths} — which\n * knows only the major and minor offsets — would answer G major, a unison\n * above, instead of D, a fifth above.\n */\nfunction tonicFifths(tonic: Note, mode: KeyMode): number {\n return tonicPosition(tonic) - (mode === 'minor' ? MINOR_TONIC_FIFTHS : 0);\n}\n\n/**\n * Whether a key is actually written on this tonic — the same question\n * {@link spelledKeyOf} asks, and answered the same way.\n *\n * A key with a signature of its own is written wherever that signature is: up\n * to seven sharps or flats, which is what makes Ab minor and G# minor two\n * spellings of one key and D# major none at all. A scale that only borrows a\n * signature is written wherever it reads without a double accidental.\n */\nfunction isWrittenTonic(tonic: Note, key: KeyScale): boolean {\n const position = tonicPosition(tonic);\n if (position < FLATTEST_TONIC_FIFTHS || position > SHARPEST_TONIC_FIFTHS) {\n return false;\n }\n return isSignatureKey(key)\n ? Math.abs(keySignatureFifths(tonic, key)) <= MAX_CONVENTIONAL_FIFTHS\n : tonicCost(tonic, key).doubles === 0;\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 G mixolydian is D major and\n * the dominant of D dorian is A minor.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask\n * decides the mode of the result.\n * @returns The key a fifth above, in the mode this key's third names.\n * @throws If the tonic or the mask is malformed, or if the resulting signature\n * falls outside [-12, 12] fifths — the dominant of a key already at +12.\n * @example\n * ```ts\n * import { dominantKeyOf, formatNote, majorKey, parseNote } from '@libraz/libcantus';\n * formatNote(dominantKeyOf(parseNote('C'), majorKey(0)).tonic); // 'G'\n * ```\n * @category Scales\n */\nexport function dominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey {\n const mode = modeOf(toKeyScale(key));\n return keyFromFifths(tonicFifths(toNoteData(tonic), mode) + 1, mode);\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 dominantKeyOf} on the tonic spelling.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask\n * decides the mode of the result.\n * @returns The key a fifth below, in the mode this key's third names.\n * @throws If the tonic or the mask is malformed, or if the resulting signature\n * falls outside [-12, 12] fifths — the subdominant of a key already at -12.\n * @example\n * ```ts\n * import { formatNote, majorKey, parseNote, subdominantKeyOf } from '@libraz/libcantus';\n * formatNote(subdominantKeyOf(parseNote('C'), majorKey(0)).tonic); // 'F'\n * ```\n * @category Scales\n */\nexport function subdominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey {\n const mode = modeOf(toKeyScale(key));\n return keyFromFifths(tonicFifths(toNoteData(tonic), mode) - 1, mode);\n}\n\n/**\n * The same sounding key written the other way round the circle of fifths, or\n * null when it has no second spelling.\n *\n * Twelve fifths is one enharmonic turn of the circle, so a tonic's alternative\n * spelling sits at its own position ±12: Db (-5) is also C# (+7). It is offered\n * only when the key is actually written there, by the same test\n * {@link spelledKeyOf} spells with. That is why C major is alone while Db major\n * and F# major each answer with their twin: Eb major's twin would be D# major\n * and its nine sharps, and pitch class 3 altered is written D# but never Eb,\n * which would need two double flats to say the same thing.\n *\n * The answer is therefore always a spelling the key is written on, which makes\n * the relation its own inverse for such a key. A key spelled some other way —\n * a tonic a transposition drove out to Ebb, say — still names its written twin,\n * so this is also the way back from a spelling no one writes.\n *\n * The relation respells rather than moves, so the mask is kept: the other\n * spelling of C# lydian is Db lydian, and its root pitch class is the one it\n * was handed.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask\n * decides the mode the tonic is read in, and is carried into the result.\n * @returns The other spelling of the key, or null when it is not written.\n * @throws If the tonic or the mask is malformed.\n * @example\n * ```ts\n * import { enharmonicKeyOf, formatNote, majorKey, parseNote } from '@libraz/libcantus';\n * const other = enharmonicKeyOf(parseNote('Db'), majorKey(1));\n * other === null ? 'none' : formatNote(other.tonic); // 'C#'\n * enharmonicKeyOf('C', majorKey(0)); // null\n * ```\n * @category Scales\n */\nexport function enharmonicKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey | null {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n assertModeMask(scale);\n for (const position of [\n tonicPosition(note) - ENHARMONIC_FIFTHS,\n tonicPosition(note) + ENHARMONIC_FIFTHS,\n ]) {\n if (position < FLATTEST_TONIC_FIFTHS || position > SHARPEST_TONIC_FIFTHS) {\n continue;\n }\n const spelled = keyFromFifths(position).tonic;\n if (!isWrittenTonic(spelled, scale)) {\n continue;\n }\n return {\n tonic: spelled,\n // The mask is the caller's; only the root is re-read, so a tonic that did\n // not spell the mask's own root still comes back consistent.\n key: { rootPc: noteToPitchClass(spelled), modeMask12: scale.modeMask12 },\n };\n }\n return null;\n}\n\n/** The relative of the dominant key: two moves along the circle, then across. */\nfunction relativeOfDominantKeyOf(tonic: Note, key: KeyScale): SpelledKey {\n const dominant = dominantKeyOf(tonic, key);\n return relativeKeyOf(dominant.tonic, dominant.key);\n}\n\n/** The relative of the subdominant key. */\nfunction relativeOfSubdominantKeyOf(tonic: Note, key: KeyScale): SpelledKey {\n const subdominant = subdominantKeyOf(tonic, key);\n return relativeKeyOf(subdominant.tonic, subdominant.key);\n}\n\n/**\n * The closely related keys, in the order {@link relatedKeysOf} reports them and\n * {@link keyRelationBetween} tests them.\n */\nconst CLOSELY_RELATED: readonly {\n relation: KeyRelation;\n of: (tonic: Note, key: KeyScale) => SpelledKey;\n}[] = [\n { relation: 'relative', of: relativeKeyOf },\n { relation: 'parallel', of: parallelKeyOf },\n { relation: 'dominant', of: dominantKeyOf },\n { relation: 'subdominant', of: subdominantKeyOf },\n { relation: 'relativeOfDominant', of: relativeOfDominantKeyOf },\n { relation: 'relativeOfSubdominant', of: relativeOfSubdominantKeyOf },\n];\n\n/**\n * The six closely related keys of a key, 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. For C major they are A minor,\n * C minor, G major, F major, E minor and D minor, in that order. Five of them\n * are written within one accidental of the key's own signature — the relative\n * shares it exactly and the other four stand one away; the parallel key does\n * not — C minor is three flats away from C major — and belongs to the set for\n * the tonic it shares rather than for the signature it carries.\n *\n * @param tonic The spelled tonic of the key, as a note name, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`.\n * @returns The six related keys, in relation order, each with its tonic\n * spelling and its `relation` tag.\n * @throws If the tonic or the mask is malformed, or if a neighbouring signature\n * falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { majorKey, parseNote, relatedKeysOf } from '@libraz/libcantus';\n * relatedKeysOf(parseNote('C'), majorKey(0)).map((related) => related.relation);\n * // ['relative', 'parallel', 'dominant', 'subdominant', 'relativeOfDominant', 'relativeOfSubdominant']\n * ```\n * @category Scales\n */\nexport function relatedKeysOf(\n tonic: NoteLike,\n key: KeyLike,\n): (SpelledKey & { relation: KeyRelation })[] {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n return CLOSELY_RELATED.map(({ relation, of }) => ({ ...of(note, scale), relation }));\n}\n\n/**\n * How the second key stands to the first, or null when they are not related.\n *\n * The relations are tested in a fixed order — identity, enharmonic spelling,\n * then the six closely related keys in the order {@link relatedKeysOf} lists\n * them — and the first match is reported. Only the identity test looks at the\n * tonic spelling; every other test compares root pitch class and mode, so\n * C# minor and Db minor both read as the relative of E major.\n *\n * Both keys are read through the mode their third names, exactly as\n * {@link relatedKeysOf} reads the key it is given, so A harmonic minor stands\n * to C major as A minor does whichever of the two is asked about. That is what\n * makes the answer symmetric: if one key's related keys name the other, the\n * relation between them is reported from either side.\n *\n * @param a The key the relation is measured from.\n * @param b The key the relation is measured to.\n * @returns The relation, or null when `b` is none of `a`'s related keys.\n * @throws If either key is malformed, or if a neighbouring signature of `a`\n * falls outside [-12, 12] fifths.\n * @example\n * ```ts\n * import { keyRelationBetween, majorKey, minorKey, parseNote } from '@libraz/libcantus';\n * const cMajor = { tonic: parseNote('C'), key: majorKey(0) };\n * keyRelationBetween(cMajor, { tonic: parseNote('A'), key: minorKey(9) }); // 'relative'\n * keyRelationBetween(cMajor, { tonic: parseNote('Eb'), key: minorKey(3) }); // null\n * ```\n * @category Scales\n */\nexport function keyRelationBetween(a: SpelledKey, b: SpelledKey): KeyRelation | null {\n assertModeMask(a.key, 'a.key');\n assertModeMask(b.key, 'b.key');\n if (soundsLike(a.key, b.key)) {\n return spelledLike(a.tonic, b.tonic) ? 'same' : 'enharmonic';\n }\n for (const { relation, of } of CLOSELY_RELATED) {\n if (soundsLike(of(a.tonic, a.key).key, b.key)) {\n return relation;\n }\n }\n return null;\n}\n","/**\n * Scale systems: which pitch organisations functional harmony was built for,\n * and which ones it would only be imposed on.\n *\n * Roman numerals, tonic/subdominant/dominant function and cadence detection all\n * presuppose Western common practice. A `KeyScale` is a root pitch class and a\n * twelve-bit mask, which says nothing about the tradition the scale came from,\n * so analysis code that wants to branch instead of assuming needs this\n * classification to branch on.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport type { ScaleName, ScaleNameInput, WorldScaleName } from './masks.js';\nimport { NAMED_SCALES, resolveScaleName, WORLD_SCALES } from './masks.js';\n\n/**\n * The kind of pitch organisation a scale belongs to.\n *\n * - `'common-practice'`: the scale functional harmony is defined on — a tonic,\n * a dominant carrying a leading tone, and a triad available on every degree.\n * - `'modal'`: a seven-note rotation or alteration of that material. Degrees\n * and Roman numerals still describe it, but a dominant-to-tonic cadence is\n * not what holds the music together.\n * - `'non-functional'`: the collection carries no chord function of its own. A\n * gapped or symmetric set takes its harmony from whatever key it is used\n * over, and a scale from a tradition organised melodically rather than by\n * chords has no functional reading at all.\n *\n * @category Scales\n */\nexport type ScaleSystem = 'common-practice' | 'modal' | 'non-functional';\n\n/**\n * The system each built-in scale belongs to.\n *\n * Every name in {@link NAMED_SCALES} and {@link WORLD_SCALES} appears here, so\n * a scale added without a classification fails to compile rather than falling\n * silently into a default. Aliases are classified through the name they resolve\n * to; use {@link scaleSystemOf} to look one up.\n *\n * @category Scales\n */\nexport const SCALE_SYSTEMS: Readonly<Record<ScaleName | WorldScaleName, ScaleSystem>> =\n Object.freeze({\n major: 'common-practice',\n ionian: 'common-practice',\n naturalMinor: 'common-practice',\n aeolian: 'common-practice',\n harmonicMinor: 'common-practice',\n melodicMinor: 'common-practice',\n dorian: 'modal',\n phrygian: 'modal',\n lydian: 'modal',\n mixolydian: 'modal',\n locrian: 'modal',\n lydianDominant: 'modal',\n mixolydianB13: 'modal',\n locrianNatural2: 'modal',\n altered: 'modal',\n phrygianDominant: 'modal',\n majorPentatonic: 'non-functional',\n minorPentatonic: 'non-functional',\n blues: 'non-functional',\n wholeTone: 'non-functional',\n octatonicHalfWhole: 'non-functional',\n octatonicWholeHalf: 'non-functional',\n chromatic: 'non-functional',\n miyakoBushi: 'non-functional',\n ritsu: 'non-functional',\n minyo: 'non-functional',\n ryukyu: 'non-functional',\n ajam: 'non-functional',\n nahawand: 'non-functional',\n kurd: 'non-functional',\n hijaz: 'non-functional',\n hijazkar: 'non-functional',\n bilaval: 'non-functional',\n khamaj: 'non-functional',\n kafi: 'non-functional',\n asavari: 'non-functional',\n bhairavi: 'non-functional',\n bhairav: 'non-functional',\n kalyan: 'non-functional',\n marwa: 'non-functional',\n purvi: 'non-functional',\n todi: 'non-functional',\n });\n\n/**\n * System of each built-in mask, Western reading first.\n *\n * Maqam Nahawand and the natural minor are the same twelve pitch classes, so a\n * mask cannot tell them apart. Building the table in name order — the Western\n * register before the world one — makes the mask answer with the Western\n * reading, which is the reading a bare `KeyScale` from key detection or from a\n * caller's own data actually carries.\n */\nconst SYSTEM_BY_MASK: ReadonlyMap<number, ScaleSystem> = (() => {\n const byMask = new Map<number, ScaleSystem>();\n const names: (ScaleName | WorldScaleName)[] = [\n ...(Object.keys(NAMED_SCALES) as ScaleName[]),\n ...(Object.keys(WORLD_SCALES) as WorldScaleName[]),\n ];\n for (const name of names) {\n const mask = Object.hasOwn(NAMED_SCALES, name)\n ? NAMED_SCALES[name as ScaleName]\n : WORLD_SCALES[name as WorldScaleName];\n if (!byMask.has(mask)) {\n byMask.set(mask, SCALE_SYSTEMS[name]);\n }\n }\n return byMask;\n})();\n\n/**\n * The system a scale belongs to.\n *\n * A name — canonical or an alias — is answered exactly: `'nahawand'` is a maqam\n * even though its mask is the natural minor's. A `KeyScale` carries no\n * tradition, so it is answered by its mask, under the Western reading of that\n * mask; a mask that is no built-in scale answers undefined.\n *\n * @param scale The scale name, or a key/scale to read the mask of.\n * @returns The system, or undefined for a mask that names no built-in scale.\n * @throws If `scale` is a string naming no built-in scale. An unknown name is a\n * typo, and answering it the way an unrecognised mask is answered would hide\n * that.\n * @example\n * ```ts\n * import { scaleSystemOf, majorKey } from '@libraz/libcantus';\n * scaleSystemOf('harmonicMinor'); // 'common-practice'\n * scaleSystemOf('dorian'); // 'modal'\n * scaleSystemOf('hijaz'); // 'non-functional'\n * scaleSystemOf(majorKey(0)); // 'common-practice'\n * ```\n * @category Scales\n */\nexport function scaleSystemOf(scale: KeyScale | ScaleNameInput): ScaleSystem | undefined {\n if (typeof scale === 'string') {\n const canonical = resolveScaleName(scale);\n if (canonical === undefined) {\n throw new InvalidInputError(`Unknown scale: ${scale}`);\n }\n return SCALE_SYSTEMS[canonical];\n }\n return SYSTEM_BY_MASK.get(scale.modeMask12);\n}\n\n/**\n * Whether functional (Roman-numeral) analysis applies to a scale.\n *\n * True for common-practice and modal scales, false for everything classed\n * `'non-functional'` — analysis that would otherwise read a raga as a chord\n * progression can ask here first.\n *\n * The two ways of naming a scale are answered differently, exactly as\n * {@link scaleSystemOf} answers them. A name carries its tradition, so\n * `'hijaz'` is false. A `KeyScale` carries only a mask, and a mask shared with\n * a Western scale is read the Western way — maqam Hijaz and the phrygian\n * dominant are the same seven pitch classes, so that mask is modal and answers\n * true. A mask that names no built-in scale answers true as well, which keeps a\n * caller's own mask being analysed the way it always has been. Pass the name\n * whenever the tradition is known and the answer should follow it.\n *\n * @param scale The scale name, or a key/scale to read the mask of.\n * @returns True when functional harmony describes the scale.\n * @throws If `scale` is a string naming no built-in scale.\n * @example\n * ```ts\n * import { supportsFunctionalHarmony, scaleByName } from '@libraz/libcantus';\n * supportsFunctionalHarmony('harmonicMinor'); // true\n * supportsFunctionalHarmony('bhairav'); // false\n * supportsFunctionalHarmony('hijaz'); // false — named, so read as a maqam\n * supportsFunctionalHarmony(scaleByName('hijaz', 0)); // true — its mask is the phrygian dominant\n * supportsFunctionalHarmony(scaleByName('ryukyu', 0)); // false — a mask no Western scale shares\n * ```\n * @category Scales\n */\nexport function supportsFunctionalHarmony(scale: KeyScale | ScaleNameInput): boolean {\n return scaleSystemOf(scale) !== 'non-functional';\n}\n","/**\n * The structured reading of a chord: what it is built from, not what it is\n * called.\n *\n * A quality name is a closed vocabulary and a lead sheet's harmony is not. A\n * chart writes a *set* of alterations over a base — `maj7#11`, `7b9#11`,\n * `sus4(add9)` — so an enumeration of names can never be complete, and every\n * name added to one is a name missing from its neighbours. A {@link ChordSpec}\n * names the parts instead: a base triad, an optional seventh, altered, added\n * and omitted degrees. A combination nothing names still has exact intervals,\n * an exact pitch-class set, and a symbol that reads back as itself.\n *\n * Each {@link ChordQuality} is an alias for one spec, so the two directions\n * cannot drift: a named chord's interval template is derived from its spec\n * rather than listed beside it.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport { assertFiniteNumber } from '../../core/validation/index.js';\n\n/**\n * Chord quality identifiers understood by the chord builder.\n *\n * Each name is an alias for one {@link ChordSpec}. The union is closed, so it\n * names the chords a chart writes most often rather than every chord that can\n * be written; build the rest from a spec.\n *\n * @category Chords\n */\nexport type ChordQuality =\n | 'maj'\n | 'min'\n | 'dim'\n | 'aug'\n | 'maj7'\n | 'min7'\n | 'dom7'\n | 'dim7'\n | 'm7b5'\n | 'minMaj7'\n | 'minMaj9'\n | 'minMaj11'\n | 'minMaj13'\n | 'aug7'\n | 'augMaj7'\n | 'majb5'\n | '6'\n | 'min6'\n | '6/9'\n | 'sus2'\n | 'sus4'\n | 'add9'\n | 'add11'\n | 'maj9'\n | 'min9'\n | 'dom9'\n | '7b9'\n | '7#9'\n | '7#11'\n | '7b13'\n | '11'\n | '13'\n | '5'\n | '7sus4'\n | '7b5'\n | '7alt'\n | '13b9'\n | 'maj13'\n | 'maj7#11'\n | 'min11'\n | 'min13'\n | 'minAdd9'\n | 'min6/9';\n\n/**\n * The triad (or dyad) a chord is built on, before any seventh or tension.\n *\n * `sus2` and `sus4` replace the third rather than sitting beside it, and\n * `power` is the bare root and fifth, so the base says which tones the chord\n * has before anything is added to them.\n *\n * @category Chords\n */\nexport type ChordBase = 'maj' | 'min' | 'dim' | 'aug' | 'sus2' | 'sus4' | 'power';\n\n/**\n * The seventh stacked over a base: major, minor, or diminished.\n *\n * @category Chords\n */\nexport type ChordSeventh = 'maj7' | 'min7' | 'dim7';\n\n/**\n * A chord degree that can be raised or lowered: the fifth and the tensions.\n *\n * @category Chords\n */\nexport type AlteredDegree = 5 | 9 | 11 | 13;\n\n/**\n * One altered degree: which one, and by how much.\n *\n * `alter: 0` is not redundant — it says the degree is *present* and unaltered,\n * which is what the natural ninth of a thirteenth chord is.\n *\n * @category Chords\n */\nexport type Alteration = {\n degree: AlteredDegree;\n alter: -1 | 0 | 1;\n};\n\n/**\n * A chord as its parts: base, seventh, alterations, additions and omissions.\n *\n * The library's structural chord model. `alterations` are tensions over a\n * seventh chord; `additions` are degrees added without one (`add9`, and the\n * sixth of a sixth chord); `omissions` are degrees the chord leaves out (the\n * third of an eleventh chord, the fifth of a shell voicing) named by chord\n * degree, so `omissions: [5]` is a fifth-less chord.\n *\n * @category Chords\n */\nexport type ChordSpec = {\n rootPc: number;\n base: ChordBase;\n seventh?: ChordSeventh;\n alterations: Alteration[];\n additions: number[];\n omissions: number[];\n bassPc?: number;\n};\n\n/** The tones each base names, as `[chord degree, semitones above the root]`. */\nconst BASE_TONES: Record<ChordBase, readonly (readonly [number, number])[]> = {\n maj: [\n [1, 0],\n [3, 4],\n [5, 7],\n ],\n min: [\n [1, 0],\n [3, 3],\n [5, 7],\n ],\n dim: [\n [1, 0],\n [3, 3],\n [5, 6],\n ],\n aug: [\n [1, 0],\n [3, 4],\n [5, 8],\n ],\n sus2: [\n [1, 0],\n [3, 2],\n [5, 7],\n ],\n sus4: [\n [1, 0],\n [3, 5],\n [5, 7],\n ],\n power: [\n [1, 0],\n [5, 7],\n ],\n};\n\n/** Semitones above the root of each seventh. */\nconst SEVENTH_SEMITONES: Record<ChordSeventh, number> = { maj7: 11, min7: 10, dim7: 9 };\n\n/** Semitones above the root of each addable/alterable degree, unaltered. */\nconst DEGREE_SEMITONES: Record<number, number> = { 6: 9, 9: 14, 11: 17, 13: 21 };\n\n/** Semitones above the root of an unaltered fifth, the tone alterations move. */\nconst PERFECT_FIFTH = 7;\n\n/** Chord degrees a chord may omit: root, third, fifth. */\nconst OMITTABLE_DEGREES = [1, 3, 5];\n\n/** The quality naming each base on its own, when nothing closer fits. */\nconst BASE_QUALITY: Record<ChordBase, ChordQuality> = {\n maj: 'maj',\n min: 'min',\n dim: 'dim',\n aug: 'aug',\n sus2: 'sus2',\n sus4: 'sus4',\n power: '5',\n};\n\n/** The parts of a quality's spec, in the order a chord is built from them. */\ntype SpecParts = {\n seventh?: ChordSeventh;\n alt?: readonly (readonly [AlteredDegree, -1 | 0 | 1])[];\n add?: readonly number[];\n omit?: readonly number[];\n};\n\n/** Build a rootless spec for the alias table, so it reads as the chord does. */\nfunction parts(base: ChordBase, spec: SpecParts = {}): ChordSpec {\n const built: ChordSpec = {\n rootPc: 0,\n base,\n alterations: (spec.alt ?? []).map(([degree, alter]) => ({ degree, alter })),\n additions: [...(spec.add ?? [])],\n omissions: [...(spec.omit ?? [])],\n };\n if (spec.seventh !== undefined) {\n built.seventh = spec.seventh;\n }\n return built;\n}\n\n/**\n * The spec each quality name stands for.\n *\n * Declaration order is stable but carries no meaning: {@link chordQualities}\n * publishes it and chord detection uses it as a final tie-break, so it is part\n * of the observable behaviour and does not get reshuffled casually.\n */\nconst QUALITY_SPECS: Record<ChordQuality, ChordSpec> = {\n maj: parts('maj'),\n min: parts('min'),\n dim: parts('dim'),\n aug: parts('aug'),\n majb5: parts('maj', { alt: [[5, -1]] }),\n maj7: parts('maj', { seventh: 'maj7' }),\n min7: parts('min', { seventh: 'min7' }),\n dom7: parts('maj', { seventh: 'min7' }),\n dim7: parts('dim', { seventh: 'dim7' }),\n m7b5: parts('dim', { seventh: 'min7' }),\n minMaj7: parts('min', { seventh: 'maj7' }),\n minMaj9: parts('min', { seventh: 'maj7', alt: [[9, 0]] }),\n minMaj11: parts('min', {\n seventh: 'maj7',\n alt: [\n [9, 0],\n [11, 0],\n ],\n }),\n minMaj13: parts('min', {\n seventh: 'maj7',\n alt: [\n [9, 0],\n [13, 0],\n ],\n }),\n aug7: parts('aug', { seventh: 'min7' }),\n augMaj7: parts('aug', { seventh: 'maj7' }),\n '6': parts('maj', { add: [6] }),\n min6: parts('min', { add: [6] }),\n '6/9': parts('maj', { add: [6, 9] }),\n sus2: parts('sus2'),\n sus4: parts('sus4'),\n add9: parts('maj', { add: [9] }),\n add11: parts('maj', { add: [11] }),\n maj9: parts('maj', { seventh: 'maj7', alt: [[9, 0]] }),\n min9: parts('min', { seventh: 'min7', alt: [[9, 0]] }),\n dom9: parts('maj', { seventh: 'min7', alt: [[9, 0]] }),\n '7b9': parts('maj', { seventh: 'min7', alt: [[9, -1]] }),\n '7#9': parts('maj', { seventh: 'min7', alt: [[9, 1]] }),\n '7#11': parts('maj', { seventh: 'min7', alt: [[11, 1]] }),\n '7b13': parts('maj', { seventh: 'min7', alt: [[13, -1]] }),\n // The eleventh chord as a chart voices it: the third gives way to the\n // eleventh a semitone above it rather than sounding against it.\n '11': parts('maj', {\n seventh: 'min7',\n alt: [\n [9, 0],\n [11, 0],\n ],\n omit: [3],\n }),\n '13': parts('maj', {\n seventh: 'min7',\n alt: [\n [9, 0],\n [13, 0],\n ],\n }),\n '5': parts('power'),\n '7sus4': parts('sus4', { seventh: 'min7' }),\n '7b5': parts('maj', { seventh: 'min7', alt: [[5, -1]] }),\n // The altered dominant as it is voiced from a lead sheet: a dominant seventh\n // with a raised fifth and a raised ninth. The full seven-note altered stack\n // has no seven-letter spelling, so it is not what this symbol denotes.\n '7alt': parts('aug', { seventh: 'min7', alt: [[9, 1]] }),\n '13b9': parts('maj', {\n seventh: 'min7',\n alt: [\n [9, -1],\n [13, 0],\n ],\n }),\n maj13: parts('maj', {\n seventh: 'maj7',\n alt: [\n [9, 0],\n [13, 0],\n ],\n }),\n 'maj7#11': parts('maj', { seventh: 'maj7', alt: [[11, 1]] }),\n min11: parts('min', {\n seventh: 'min7',\n alt: [\n [9, 0],\n [11, 0],\n ],\n }),\n min13: parts('min', {\n seventh: 'min7',\n alt: [\n [9, 0],\n [13, 0],\n ],\n }),\n minAdd9: parts('min', { add: [9] }),\n 'min6/9': parts('min', { add: [6, 9] }),\n};\n\n/**\n * Reduce a spec to the one form that stands for its harmony.\n *\n * Two specs that name the same chord must compare equal, or a name lookup would\n * miss the chord it holds: an augmented fifth over a major triad *is* the\n * augmented triad, and a natural ninth over a chord with no seventh *is* an\n * added ninth. Normalizing is what lets `C7(#5)` and `C7#5` reach one spec, and\n * `C(9)` reach the same spec as `Cadd9`.\n *\n * Not part of the package's public surface: every spec the library hands out is\n * already normalized.\n *\n * @param spec The spec to reduce.\n * @returns An equivalent spec in canonical form.\n */\nexport function normalizeChordSpec(spec: ChordSpec): ChordSpec {\n let base = spec.base;\n const alterations = new Map<AlteredDegree, -1 | 0 | 1>();\n for (const alteration of spec.alterations ?? []) {\n alterations.set(alteration.degree, alteration.alter);\n }\n const additions = new Set(spec.additions ?? []);\n const omissions = new Set(spec.omissions ?? []);\n const fifth = alterations.get(5);\n if (fifth !== undefined) {\n // The alteration is the statement about the fifth, so a base that already\n // alters it steps back to its plain form before the two are combined.\n if (base === 'aug') {\n base = 'maj';\n } else if (base === 'dim') {\n base = 'min';\n }\n if (fifth === 0) {\n alterations.delete(5);\n } else if (base === 'maj' && fifth === 1) {\n base = 'aug';\n alterations.delete(5);\n } else if (base === 'min' && fifth === -1) {\n base = 'dim';\n alterations.delete(5);\n }\n }\n if (spec.seventh === undefined) {\n // Without a seventh under it, an unaltered upper degree is an added tone\n // rather than a tension: C(9) is the chord Cadd9 names.\n for (const [degree, alter] of [...alterations]) {\n if (degree !== 5 && alter === 0) {\n alterations.delete(degree);\n additions.add(degree);\n }\n }\n }\n const normalized: ChordSpec = {\n rootPc: spec.rootPc,\n base,\n alterations: [...alterations]\n .sort((a, b) => a[0] - b[0])\n .map(([degree, alter]) => ({ degree, alter })),\n additions: [...additions].sort((a, b) => a - b),\n omissions: [...omissions].sort((a, b) => a - b),\n };\n if (spec.seventh !== undefined) {\n normalized.seventh = spec.seventh;\n }\n if (spec.bassPc !== undefined) {\n normalized.bassPc = spec.bassPc;\n }\n return normalized;\n}\n\n/**\n * Derive the tones of a spec that is already validated and normalized.\n *\n * Not part of the package's public surface, and the one derivation that skips\n * {@link assertChordSpec}: the alias table below is built from it before the\n * checker's own tables are complete, and every other caller reaches it through\n * a public entry point that has already checked its spec.\n *\n * @param normalized A normalized spec whose parts the model defines.\n * @returns Semitone offsets above the root, ascending and deduplicated.\n */\nfunction specIntervals(normalized: ChordSpec): number[] {\n const tones = new Map<number, number>(BASE_TONES[normalized.base]);\n for (const { degree, alter } of normalized.alterations) {\n if (degree === 5 && tones.has(5)) {\n tones.set(5, PERFECT_FIFTH + alter);\n }\n }\n for (const degree of normalized.omissions) {\n tones.delete(degree);\n }\n const offsets = new Set(tones.values());\n if (normalized.seventh !== undefined) {\n offsets.add(SEVENTH_SEMITONES[normalized.seventh]);\n }\n for (const { degree, alter } of normalized.alterations) {\n if (degree !== 5) {\n offsets.add((DEGREE_SEMITONES[degree] ?? 0) + alter);\n }\n }\n for (const degree of normalized.additions) {\n offsets.add(DEGREE_SEMITONES[degree] ?? 0);\n }\n return [...offsets].sort((a, b) => a - b);\n}\n\n/**\n * The semitone offsets above the root a spec sounds, ascending.\n *\n * The library's single derivation of chord tones: a name contributes nothing\n * here, so a combination of alterations no name covers yields its intervals the\n * same way a `maj7` does.\n *\n * @param spec The chord spec.\n * @returns Semitone offsets above the root, ascending and deduplicated.\n * @throws If any part of the spec is not one the model defines\n * ({@link InvalidInputError}), exactly as {@link chordFromSpec} rejects it.\n *\n * @example\n * ```ts\n * import { chordSpecIntervals } from '@libraz/libcantus';\n * chordSpecIntervals({\n * rootPc: 0,\n * base: 'maj',\n * seventh: 'min7',\n * alterations: [{ degree: 9, alter: -1 }, { degree: 11, alter: 1 }],\n * additions: [],\n * omissions: [],\n * });\n * // [0, 4, 7, 10, 13, 18] — the C7(b9,#11) no quality name covers\n * ```\n *\n * @category Chords\n */\nexport function chordSpecIntervals(spec: ChordSpec): number[] {\n return specIntervals(assertChordSpec(spec));\n}\n\n/** Semitone offsets from the root for each supported chord quality. */\nexport const QUALITY_INTERVALS: Record<ChordQuality, number[]> = Object.fromEntries(\n (Object.keys(QUALITY_SPECS) as ChordQuality[]).map((quality) => [\n quality,\n specIntervals(normalizeChordSpec(QUALITY_SPECS[quality])),\n ]),\n) as Record<ChordQuality, number[]>;\n\n/** A spec's identity as text, so two readings of one chord compare equal. */\nfunction specKey(spec: ChordSpec): string {\n const normalized = normalizeChordSpec(spec);\n return [\n normalized.base,\n normalized.seventh ?? '',\n normalized.alterations.map(({ degree, alter }) => `${alter}:${degree}`).join('.'),\n normalized.additions.join('.'),\n normalized.omissions.join('.'),\n ].join('|');\n}\n\n/** The quality naming each spec exactly, keyed by the spec's own identity. */\nconst QUALITY_BY_KEY: ReadonlyMap<string, ChordQuality> = new Map(\n (Object.keys(QUALITY_SPECS) as ChordQuality[]).map((quality) => [\n specKey(QUALITY_SPECS[quality]),\n quality,\n ]),\n);\n\n/**\n * A copy of the spec a quality name stands for, rooted on `rootPc`.\n *\n * @param quality The quality name.\n * @param rootPc Root pitch class to root the spec on.\n * @param bassPc Optional slash-chord bass pitch class.\n * @returns The spec, freshly built so the alias table is never shared.\n * @throws If the quality is not a known name.\n *\n * @category Chords\n */\nexport function chordSpecForQuality(\n quality: ChordQuality,\n rootPc: number,\n bassPc?: number,\n): ChordSpec {\n if (!Object.hasOwn(QUALITY_SPECS, quality)) {\n throw new InvalidInputError(`Unknown chord quality: ${String(quality)}`);\n }\n const spec = normalizeChordSpec(QUALITY_SPECS[quality]);\n spec.rootPc = rootPc;\n if (bassPc !== undefined) {\n spec.bassPc = bassPc;\n }\n return spec;\n}\n\n/**\n * The quality naming a spec exactly, or undefined when none does.\n *\n * Not part of the package's public surface: {@link chordSpecQuality} is the\n * total answer callers want, and this is the half of it that admits there is no\n * name.\n *\n * @param spec The chord spec.\n * @returns The quality name, or undefined.\n */\nexport function exactChordSpecQuality(spec: ChordSpec): ChordQuality | undefined {\n return QUALITY_BY_KEY.get(specKey(spec));\n}\n\n/**\n * The quality name a spec is reported under.\n *\n * A spec no name covers still has to answer, because `quality` is a required\n * field of every {@link Chord} and of the records that travel with one. The\n * answer is the *nearest* name: the largest named chord whose every tone this\n * spec also sounds, and failing that the name of its base. Nothing is lost by\n * it — the spec and the interval template still carry the alterations, and a\n * symbol is written from those rather than from this name.\n *\n * @param spec The chord spec.\n * @returns The name that fits the spec exactly, or the nearest one that fits\n * inside it.\n * @throws If any part of the spec is not one the model defines\n * ({@link InvalidInputError}), exactly as {@link chordFromSpec} rejects it.\n *\n * @example\n * ```ts\n * import { chordSpecQuality, parseChordSymbol } from '@libraz/libcantus';\n * parseChordSymbol('C7(b9,#11)').quality; // '7b9' — the nearest name\n * chordSpecQuality({ rootPc: 0, base: 'maj', seventh: 'min7', alterations: [],\n * additions: [], omissions: [] }); // 'dom7'\n * ```\n *\n * @category Chords\n */\nexport function chordSpecQuality(spec: ChordSpec): ChordQuality {\n const checked = assertChordSpec(spec);\n const exact = exactChordSpecQuality(checked);\n if (exact !== undefined) {\n return exact;\n }\n const offsets = new Set(specIntervals(checked));\n let nearest: ChordQuality | undefined;\n let size = 0;\n for (const quality of Object.keys(QUALITY_INTERVALS) as ChordQuality[]) {\n const template = QUALITY_INTERVALS[quality];\n if (template.length > size && template.every((offset) => offsets.has(offset))) {\n nearest = quality;\n size = template.length;\n }\n }\n return nearest ?? BASE_QUALITY[checked.base];\n}\n\n/** The degree and alteration each compound semitone offset names. */\nconst EXTENSION_BY_SEMITONE: ReadonlyMap<number, Alteration> = new Map([\n [13, { degree: 9, alter: -1 } as Alteration],\n [14, { degree: 9, alter: 0 } as Alteration],\n [15, { degree: 9, alter: 1 } as Alteration],\n [16, { degree: 11, alter: -1 } as Alteration],\n [17, { degree: 11, alter: 0 } as Alteration],\n [18, { degree: 11, alter: 1 } as Alteration],\n [20, { degree: 13, alter: -1 } as Alteration],\n [21, { degree: 13, alter: 0 } as Alteration],\n [22, { degree: 13, alter: 1 } as Alteration],\n]);\n\n/** Whether two ascending offset lists name the same tones. */\nfunction sameOffsets(a: readonly number[], b: readonly number[]): boolean {\n return a.length === b.length && a.every((offset, index) => offset === b[index]);\n}\n\n/**\n * Read an interval template structurally, or undefined when it has no reading.\n *\n * The inverse of {@link chordSpecIntervals}, and checked against it: a template\n * whose reading does not rebuild the template exactly has no spec, which is how\n * an arbitrary set of offsets — a cluster, a voicing recorded as a chord — is\n * kept from being reported as a chord it is not.\n *\n * Not part of the package's public surface: `chordSpecOf` is the entry point,\n * and it has a chord's quality to fall back on when this finds no reading.\n *\n * @param intervals Semitone offsets above the root.\n * @returns The spec (rooted on pitch class 0), or undefined.\n */\nexport function chordSpecFromIntervals(intervals: readonly number[]): ChordSpec | undefined {\n const offsets = [...new Set(intervals)].sort((a, b) => a - b);\n const has = (offset: number): boolean => offsets.includes(offset);\n const alterations: Alteration[] = [];\n const additions: number[] = [];\n const omissions: number[] = [];\n if (!has(0)) {\n omissions.push(1);\n }\n const third = has(4) ? 4 : has(3) ? 3 : has(5) ? 5 : has(2) ? 2 : undefined;\n const fifth = has(7) ? 7 : has(6) ? 6 : has(8) ? 8 : undefined;\n let base: ChordBase = 'maj';\n if (third === 3) {\n base = 'min';\n } else if (third === 5) {\n base = 'sus4';\n } else if (third === 2) {\n base = 'sus2';\n } else if (third === undefined) {\n // A bare root and fifth is the power chord itself; anything more is a chord\n // that left its third out.\n if (fifth === 7 && offsets.length === 2 && has(0)) {\n base = 'power';\n } else {\n omissions.push(3);\n }\n }\n if (fifth === undefined) {\n omissions.push(5);\n } else if (fifth !== 7) {\n if (base === 'maj' && fifth === 8) {\n base = 'aug';\n } else if (base === 'min' && fifth === 6) {\n base = 'dim';\n } else {\n alterations.push({ degree: 5, alter: fifth === 6 ? -1 : 1 });\n }\n }\n let seventh: ChordSeventh | undefined = has(11) ? 'maj7' : has(10) ? 'min7' : undefined;\n if (has(9)) {\n // Nine semitones is the diminished seventh only over a diminished triad\n // with no seventh of its own; anywhere else it is the added sixth.\n if (seventh === undefined && base === 'dim') {\n seventh = 'dim7';\n } else {\n additions.push(6);\n }\n }\n for (const offset of offsets) {\n if (offset <= 12) {\n continue;\n }\n const extension = EXTENSION_BY_SEMITONE.get(offset);\n if (extension === undefined) {\n return undefined;\n }\n alterations.push(extension);\n }\n const spec: ChordSpec = { rootPc: 0, base, alterations, additions, omissions };\n if (seventh !== undefined) {\n spec.seventh = seventh;\n }\n const normalized = normalizeChordSpec(spec);\n return sameOffsets(specIntervals(normalized), offsets) ? normalized : undefined;\n}\n\n/** Reject a value that is not one of a fixed set of spec parts. */\nfunction assertPart<T extends string>(value: T, allowed: readonly T[], name: string): T {\n if (!allowed.includes(value)) {\n throw new InvalidInputError(\n `${name} must be one of ${allowed.join(', ')}; received ${JSON.stringify(value)}`,\n );\n }\n return value;\n}\n\n/** Reject a degree a spec cannot alter, add, or omit. */\nfunction assertDegreeIn(value: number, allowed: readonly number[], name: string): number {\n if (!allowed.includes(value)) {\n throw new InvalidInputError(\n `${name} must be one of ${allowed.join(', ')}; received ${String(value)}`,\n );\n }\n return value;\n}\n\n/** Reject a list of degrees that is not a list, before it is iterated. */\nfunction assertDegreeList(\n value: readonly number[],\n allowed: readonly number[],\n name: string,\n): void {\n if (!Array.isArray(value)) {\n throw new InvalidInputError(`${name} must be an array; received ${typeof value}`);\n }\n for (const degree of value) {\n assertDegreeIn(degree, allowed, name);\n }\n}\n\n/**\n * Validate a spec and return it in canonical form.\n *\n * A spec arrives from a caller as plain data, so every part is checked before\n * any of it reaches the interval derivation: an unknown base has no tones, an\n * alteration of a degree the chord cannot alter would silently contribute\n * nothing, and a list of degrees that is not a list would fail as a `TypeError`\n * from inside the derivation rather than as this library's own error.\n *\n * @param spec The spec to check.\n * @returns The normalized spec.\n * @throws If the spec is not an object, if `alterations`, `additions` or\n * `omissions` is not an array, or if any part of the spec is not one the\n * model defines ({@link InvalidInputError}).\n *\n * @category Chords\n */\nexport function assertChordSpec(spec: ChordSpec): ChordSpec {\n if (spec === null || typeof spec !== 'object') {\n throw new InvalidInputError(`chord spec must be an object; received ${typeof spec}`);\n }\n assertFiniteNumber(spec.rootPc, 'chord spec rootPc');\n if (spec.bassPc !== undefined) {\n assertFiniteNumber(spec.bassPc, 'chord spec bassPc');\n }\n assertPart(spec.base, Object.keys(BASE_TONES) as ChordBase[], 'chord spec base');\n if (spec.seventh !== undefined) {\n assertPart(\n spec.seventh,\n Object.keys(SEVENTH_SEMITONES) as ChordSeventh[],\n 'chord spec seventh',\n );\n }\n if (!Array.isArray(spec.alterations)) {\n throw new InvalidInputError('chord spec alterations must be an array');\n }\n for (const alteration of spec.alterations) {\n assertDegreeIn(alteration?.degree, [5, 9, 11, 13], 'chord spec alteration degree');\n assertDegreeIn(alteration.alter, [-1, 0, 1], 'chord spec alteration');\n }\n assertDegreeList(spec.additions, [6, 9, 11, 13], 'chord spec additions');\n assertDegreeList(spec.omissions, OMITTABLE_DEGREES, 'chord spec omissions');\n return normalizeChordSpec(spec);\n}\n","import { InvalidInputError } from '../../core/errors/index.js';\nimport {\n diatonicLetterOf,\n noteToPitchClass,\n pitchClassOf as pitchClass,\n} from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { assertDegree, assertFiniteNumber, assertInteger } from '../../core/validation/index.js';\nimport { type KeyLike, scaleTonesInDegreeOrder, toKeyScale } from '../scale/index.js';\nimport type { ChordQuality, ChordSpec } from './spec.js';\nimport {\n assertChordSpec,\n chordSpecForQuality,\n chordSpecFromIntervals,\n chordSpecIntervals,\n chordSpecQuality,\n QUALITY_INTERVALS,\n} from './spec.js';\n\nexport type {\n Alteration,\n AlteredDegree,\n ChordBase,\n ChordQuality,\n ChordSeventh,\n ChordSpec,\n} from './spec.js';\nexport { chordSpecIntervals, chordSpecQuality } from './spec.js';\n\n/**\n * The basic role a chord tone plays: the degrees a chord's own template names.\n *\n * The shared vocabulary behind {@link chordToneRole}, the harmony layer's\n * `HarmonyRole`, and the voice analyser's labels, so the three cannot drift.\n *\n * @category Chords\n */\nexport type ChordToneRole = 'root' | 'third' | 'fifth' | 'sixth' | 'seventh';\n\n/**\n * A bare spelled pitch used as an enharmonic hint: a diatonic letter\n * (0..6 = C..B) plus a chromatic alteration (-2 double-flat .. +2 double-sharp).\n * Mirrors the letter/alter half of the pitch module's `Note` without an octave.\n *\n * @category Chords\n */\nexport type PitchSpelling = {\n letter: number;\n alter: number;\n};\n\n/**\n * A chord expressed as a root pitch class plus semitone offsets.\n *\n * `intervals` is what the chord sounds and `quality` is what it is called. The\n * two are not equally expressive: a chart writes any set of alterations over a\n * base, while {@link ChordQuality} is a closed list of names, so `quality` is a\n * *derived* label — the name that fits the chord exactly, or else the nearest\n * name inside it (see {@link chordSpecQuality}). Read the structure with\n * {@link chordSpecOf} rather than the name when the alterations matter; that is\n * also what {@link formatChordSymbol} does, so a chord no name covers still\n * writes itself out in full.\n *\n * `rootSpelling`/`bassSpelling` are optional enharmonic hints recorded by\n * parsers (e.g. `parseChordSymbol('Bb7')`) so formatters can reproduce the\n * original spelling instead of defaulting to sharps. Consumers may ignore\n * them; a hint is only trusted when its pitch class still matches the\n * corresponding `rootPc`/`bassPc`.\n *\n * `toneSpellings` is the same kind of hint for the tones themselves, one\n * spelling per entry in `intervals`. It exists for the chords whose letters do\n * not follow from stacking thirds: an augmented sixth is ten semitones like a\n * minor seventh but is five letters above its root rather than six, and no\n * {@link ChordQuality} can say so. Builders that know better attach it, and\n * {@link spellChordFromRoot} reads the letter distances back out of it; a hint\n * is trusted as a whole or not at all, and only while every spelling still\n * names the pitch class its interval names.\n *\n * @category Chords\n */\nexport type Chord = {\n rootPc: number;\n quality: ChordQuality;\n intervals: number[];\n bassPc?: number;\n rootSpelling?: PitchSpelling;\n bassSpelling?: PitchSpelling;\n toneSpellings?: PitchSpelling[];\n};\n\n/**\n * A chord positioned in time: a root pitch class and quality placed at a beat,\n * with optional harmonic-analysis hints. Serves as the neutral exchange record\n * between progression generators and timeline analysers.\n *\n * @category Composition\n */\nexport type ChordSpan = {\n rootPc: number;\n quality: ChordQuality;\n startBeat: number;\n bassPc?: number;\n /**\n * Semitone offsets from the root, for a chord the {@link ChordQuality} union\n * cannot name exactly. Omitted for a standard chord: the template is then\n * derived from `quality`, which is what every producer and consumer did\n * before the field existed. Read a span with {@link chordFromSpan} so either\n * case yields the same chord.\n */\n intervals?: number[];\n /** 1-based scale degree of the chord root, when known. */\n degree?: number;\n /** True when the chord is a secondary dominant tonicizing another degree. */\n secondaryDominant?: boolean;\n};\n\n/**\n * A chord occupying a half-open beat span `[startBeat, endBeat)`.\n *\n * The exchange record between anything that lays chords out in time — a\n * timeline analysis, a bass-line plan, an accompaniment generator — so those\n * surfaces share one definition instead of agreeing by coincidence.\n *\n * @category Composition\n */\nexport type ChordSegment = {\n startBeat: number;\n endBeat: number;\n chord: Chord;\n};\n\n/** Widest semitone offset a custom interval template may name: one MIDI range. */\nconst MAX_CHORD_INTERVAL = 127;\n\n/**\n * All chord qualities the builder understands, in declaration order.\n *\n * The order is stable but carries no meaning: it is neither alphabetical nor\n * sorted by template size. {@link detectChord} uses it as its final tie-break,\n * so it is part of the observable behaviour and does not get reshuffled\n * casually — but do not read it as a ranking.\n *\n * @category Chords\n */\nexport function chordQualities(): ChordQuality[] {\n return Object.keys(QUALITY_INTERVALS) as ChordQuality[];\n}\n\n/**\n * Build a chord rooted on a diatonic scale degree.\n *\n * The root pitch class is the degree's diatonic pitch class in `key`; the\n * quality's interval template is attached unchanged. Degrees beyond the scale\n * length wrap around, so degree 8 of a heptatonic scale is its degree 1.\n *\n * @param degree 1-based scale degree of the chord root: 1 is the tonic.\n * @param ext Chord quality to apply.\n * @param key Key context supplying the diatonic root, as a key name, a\n * key/scale, or a `Key`.\n * @returns The constructed chord.\n * @throws If `degree` is not a positive integer, or `ext` is not a known\n * quality.\n *\n * @category Chords\n */\nexport function chordFromDegree(degree: number, ext: ChordQuality, key: KeyLike): Chord {\n // Degree 0 is rejected rather than wrapped onto the last degree: a caller\n // still counting from zero gets an error instead of a plausible chord.\n assertDegree(degree, 'chord degree');\n if (!Object.hasOwn(QUALITY_INTERVALS, ext)) {\n throw new InvalidInputError(`Unknown chord quality: ${String(ext)}`);\n }\n const scale = toKeyScale(key);\n const tones = scaleTonesInDegreeOrder(scale);\n const length = tones.length;\n const index = length > 0 ? (degree - 1) % length : 0;\n const rootPc = tones[index] ?? pitchClass(scale.rootPc);\n return { rootPc, quality: ext, intervals: [...QUALITY_INTERVALS[ext]] };\n}\n\n/**\n * Build a chord from an explicit root pitch class and quality.\n *\n * @param rootPc Root pitch class (0..11).\n * @param quality Chord quality supplying the interval template.\n * @param bassPc Optional slash-chord bass pitch class.\n * @returns The constructed chord.\n *\n * @example\n * ```ts\n * import { makeChord } from '@libraz/libcantus';\n * const cmaj7 = makeChord(0, 'maj7');\n * // { rootPc: 0, quality: 'maj7', intervals: [0, 4, 7, 11] }\n * ```\n *\n * @category Chords\n */\nexport function makeChord(rootPc: number, quality: ChordQuality, bassPc?: number): Chord {\n // A NaN root formats as 'C' and an unknown quality as 'Cundefined', so both\n // travel on into voicing, detection, and serialization as a chord that looks\n // real. They are rejected where the chord is built instead.\n assertFiniteNumber(rootPc, 'chord rootPc');\n if (bassPc !== undefined) {\n assertFiniteNumber(bassPc, 'chord bassPc');\n }\n if (!Object.hasOwn(QUALITY_INTERVALS, quality)) {\n throw new InvalidInputError(`Unknown chord quality: ${String(quality)}`);\n }\n const chord: Chord = {\n rootPc: pitchClass(rootPc),\n quality,\n intervals: [...(QUALITY_INTERVALS[quality] ?? [])],\n };\n if (bassPc !== undefined) {\n chord.bassPc = pitchClass(bassPc);\n }\n return chord;\n}\n\n/**\n * Build a chord from a {@link ChordSpec}.\n *\n * The structural way in, for the chords a quality name cannot reach: the tones\n * come from the spec, and `quality` is the name that fits it, or the nearest\n * name when none does. A chord built this way is an ordinary {@link Chord} —\n * every consumer reads its `intervals` as before, and\n * {@link formatChordSymbol} writes the alterations back out.\n *\n * @param spec The chord spec; its `rootPc`/`bassPc` become the chord's.\n * @returns The constructed chord.\n * @throws If any part of the spec is not one the model defines.\n *\n * @example\n * ```ts\n * import { chordFromSpec } from '@libraz/libcantus';\n * chordFromSpec({\n * rootPc: 0,\n * base: 'maj',\n * seventh: 'maj7',\n * alterations: [{ degree: 11, alter: 1 }],\n * additions: [],\n * omissions: [],\n * });\n * // { rootPc: 0, quality: 'maj7#11', intervals: [0, 4, 7, 11, 18] }\n * ```\n *\n * @category Chords\n */\nexport function chordFromSpec(spec: ChordSpec): Chord {\n const checked = assertChordSpec(spec);\n const chord: Chord = {\n rootPc: pitchClass(checked.rootPc),\n quality: chordSpecQuality(checked),\n intervals: chordSpecIntervals(checked),\n };\n if (checked.bassPc !== undefined) {\n chord.bassPc = pitchClass(checked.bassPc);\n }\n return chord;\n}\n\n/**\n * Read a chord structurally: which base, seventh, alterations and omissions it\n * is made of.\n *\n * The reading comes from the tones themselves, so it describes a chord whatever\n * built it — a parsed symbol, a detected pitch set, a span carrying a template\n * no name covers. Only a template with no structural reading at all falls back\n * to the one its `quality` names.\n *\n * @param chord The chord to read.\n * @returns The spec, carrying the chord's own root and slash bass.\n * @throws If the chord's template has no reading and its quality is unknown.\n *\n * @example\n * ```ts\n * import { chordSpecOf, parseChordSymbol } from '@libraz/libcantus';\n * chordSpecOf(parseChordSymbol('C7(b9,#11)')).alterations;\n * // [{ degree: 9, alter: -1 }, { degree: 11, alter: 1 }]\n * ```\n *\n * @category Chords\n */\nexport function chordSpecOf(chord: Chord): ChordSpec {\n assertFiniteNumber(chord.rootPc, 'chord rootPc');\n const rootPc = pitchClass(chord.rootPc);\n const bassPc = chord.bassPc === undefined ? undefined : pitchClass(chord.bassPc);\n // A chord with no tones at all says nothing about its own structure, so the\n // name it carries is the only thing left to read it by.\n const intervals = chord.intervals ?? [];\n const read = intervals.length === 0 ? undefined : chordSpecFromIntervals(intervals);\n if (read === undefined) {\n return chordSpecForQuality(chord.quality, rootPc, bassPc);\n }\n read.rootPc = rootPc;\n if (bassPc !== undefined) {\n read.bassPc = bassPc;\n }\n return read;\n}\n\n/**\n * Validate a span's custom interval template and return a defensive copy.\n *\n * Such a template reaches the library without passing any builder, so it is\n * checked here: a fractional offset would place a chord tone between pitch\n * classes, and a hole in the array would read as an undefined chord tone in\n * every consumer downstream.\n */\nfunction copySpanIntervals(intervals: readonly number[]): number[] {\n const name = 'chord span intervals';\n if (!Array.isArray(intervals)) {\n throw new InvalidInputError(`${name} must be an array; received ${typeof intervals}`);\n }\n if (intervals.length === 0) {\n throw new InvalidInputError(`${name} must name at least one interval`);\n }\n const copy: number[] = [];\n for (let index = 0; index < intervals.length; index += 1) {\n const interval = intervals[index];\n if (interval === undefined) {\n throw new InvalidInputError(\n `${name}[${index}] must be a semitone offset; received undefined`,\n );\n }\n copy.push(\n assertInteger(interval, `${name}[${index}]`, -MAX_CHORD_INTERVAL, MAX_CHORD_INTERVAL),\n );\n }\n return copy;\n}\n\n/** Whether a chord's template departs from the one its quality names. */\nfunction hasCustomIntervals(chord: Chord): boolean {\n return !matchesQualityIntervals(chord.quality, chord.intervals);\n}\n\n/**\n * Read a {@link ChordSpan} as a chord.\n *\n * The canonical way to cross from a span to a chord. A span carrying no\n * `intervals` yields exactly the chord {@link makeChord} builds from its root,\n * quality and bass; a span carrying an explicit template keeps it, so a color\n * tone the {@link ChordQuality} union cannot name survives the trip through a\n * progression, a timeline, or a generator. The template is copied, so the\n * chord and the span never share an array.\n *\n * @param span The span to read.\n * @returns The chord the span describes.\n * @throws If the span's root, bass or quality is invalid, or its `intervals`\n * are not a non-empty array of integer semitone offsets.\n *\n * @example\n * ```ts\n * import { chordFromSpan } from '@libraz/libcantus';\n * chordFromSpan({ rootPc: 0, quality: 'maj7', startBeat: 0 });\n * // { rootPc: 0, quality: 'maj7', intervals: [0, 4, 7, 11] }\n * chordFromSpan({ rootPc: 0, quality: 'maj7', startBeat: 0, intervals: [0, 4, 11, 18] });\n * // the same span with a fifth-less, #11 voicing the quality cannot name\n * ```\n *\n * @category Composition\n */\nexport function chordFromSpan(span: ChordSpan): Chord {\n const chord = makeChord(span.rootPc, span.quality, span.bassPc);\n if (span.intervals !== undefined) {\n chord.intervals = copySpanIntervals(span.intervals);\n }\n return chord;\n}\n\n/**\n * Place a chord at a beat as a {@link ChordSpan}.\n *\n * The interval template is recorded only when it departs from the one the\n * chord's quality names, so a span built from a standard chord carries the\n * same fields it always did while a custom template is not lost.\n *\n * @param chord The chord to place.\n * @param startBeat Beat the chord starts on.\n * @returns The span describing that chord at that beat.\n * @throws If `startBeat` is not finite, or the chord's quality is unknown.\n *\n * @example\n * ```ts\n * import { makeChord, spanFromChord } from '@libraz/libcantus';\n * spanFromChord(makeChord(0, 'maj7'), 4);\n * // { rootPc: 0, quality: 'maj7', startBeat: 4 } — no intervals for a standard chord\n * ```\n *\n * @category Composition\n */\nexport function spanFromChord(chord: Chord, startBeat: number): ChordSpan {\n assertFiniteNumber(startBeat, 'chord span startBeat');\n if (!Object.hasOwn(QUALITY_INTERVALS, chord.quality)) {\n throw new InvalidInputError(`Unknown chord quality: ${String(chord.quality)}`);\n }\n const span: ChordSpan = { rootPc: chord.rootPc, quality: chord.quality, startBeat };\n if (chord.bassPc !== undefined) {\n span.bassPc = chord.bassPc;\n }\n if (hasCustomIntervals(chord)) {\n span.intervals = [...chord.intervals];\n }\n return span;\n}\n\n/**\n * Transpose a chord by a number of semitones.\n *\n * The interval template is carried over untouched, so a chord whose intervals\n * were customised keeps them, and a slash bass moves with the root. Enharmonic\n * spelling hints are dropped rather than shifted: the letter a transposed root\n * should take depends on the destination key, which a semitone count does not\n * carry. Pass the result through {@link spellChord} to respell it.\n *\n * @param chord The chord to transpose.\n * @param semitones The signed semitone offset.\n * @returns The transposed chord.\n * @throws If `semitones` is not finite.\n *\n * @example\n * ```ts\n * import { formatChordSymbol, parseChordSymbol, transposeChord } from '@libraz/libcantus';\n * formatChordSymbol(transposeChord(parseChordSymbol('C/G'), 2)); // 'D/A'\n * ```\n *\n * @category Chords\n */\nexport function transposeChord(chord: Chord, semitones: number): Chord {\n assertFiniteNumber(semitones, 'semitones');\n const steps = Math.round(semitones);\n const moved: Chord = {\n rootPc: pitchClass(chord.rootPc + steps),\n quality: chord.quality,\n intervals: [...chord.intervals],\n };\n if (chord.bassPc !== undefined) {\n moved.bassPc = pitchClass(chord.bassPc + steps);\n }\n return moved;\n}\n\n/**\n * A chord's per-tone spelling hints, when they still describe its own tones.\n *\n * The hint is trusted as a whole or not at all: it must name one spelling per\n * interval, and each spelling must still sound the pitch class its interval\n * names. A hint that has fallen out of step with the template — one left behind\n * by a transposition that moved the intervals and not the letters — is dropped\n * rather than half-applied, the same discipline a stale `rootSpelling` gets.\n *\n * Not part of the package's public surface: the readers of the hint\n * ({@link spellChordFromRoot} and the class API) share this check so they\n * cannot disagree about which hints are usable.\n */\nexport function chordToneSpellings(chord: Chord): PitchSpelling[] | undefined {\n const hints = chord.toneSpellings;\n if (hints === undefined || hints.length !== chord.intervals.length) {\n return undefined;\n }\n const usable: PitchSpelling[] = [];\n for (let index = 0; index < hints.length; index += 1) {\n const hint = hints[index];\n const interval = chord.intervals[index];\n if (hint === undefined || interval === undefined) {\n return undefined;\n }\n if (noteToPitchClass(hint) !== pitchClass(chord.rootPc + interval)) {\n return undefined;\n }\n usable.push({ letter: diatonicLetterOf(hint.letter), alter: hint.alter });\n }\n return usable;\n}\n\n/**\n * Get the sorted, deduplicated pitch classes of a chord.\n *\n * A slash bass is one of them: `F/G` sounds a G, the voicers put it in the\n * bass, and a set that omitted it would not re-detect as the chord it came\n * from. Pass `includeBass: false` to enumerate the interval template alone.\n *\n * @param chord The chord to enumerate.\n * @param opts Set `includeBass: false` to omit a slash bass.\n * @returns The chord's pitch classes, sorted ascending in [0, 11].\n *\n * @example\n * ```ts\n * import { makeChord, chordPitchClasses } from '@libraz/libcantus';\n * chordPitchClasses(makeChord(0, 'maj7')); // [0, 4, 7, 11]\n * chordPitchClasses(makeChord(5, 'maj', 7)); // [0, 5, 7, 9] — F/G, G included\n * ```\n *\n * @category Chords\n */\nexport function chordPitchClasses(chord: Chord, opts: { includeBass?: boolean } = {}): number[] {\n const set = new Set<number>();\n for (const interval of chord.intervals) {\n set.add(pitchClass(chord.rootPc + interval));\n }\n if (opts.includeBass !== false && chord.bassPc !== undefined) {\n set.add(pitchClass(chord.bassPc));\n }\n return [...set].sort((a, b) => a - b);\n}\n\n/**\n * A pitch's interval class above the chord root, in [0, 11].\n *\n * The shared reading of \"where does this pitch sit in the chord\", used by the\n * safety evaluator and the voice analyser alike so they cannot disagree.\n *\n * @param pitch MIDI pitch or bare pitch class.\n * @param chord The chord providing the root reference.\n * @returns The interval class above the root.\n * @category Chords\n */\nexport function intervalAboveRoot(pitch: number, chord: Chord): number {\n return (((pitchClass(pitch) - pitchClass(chord.rootPc)) % 12) + 12) % 12;\n}\n\n/**\n * Whether a pitch is one of a chord's tones, ignoring octave.\n *\n * @param pitch MIDI pitch or bare pitch class.\n * @param chord The chord, or null for no sounding harmony.\n * @returns True when the pitch class belongs to the chord.\n * @category Chords\n */\nexport function isChordMember(pitch: number, chord: Chord | null): boolean {\n return chord === null ? false : chordPitchClasses(chord).includes(pitchClass(pitch));\n}\n\n/** The chord-tone offsets present in a chord, reduced to pitch-class space. */\nfunction chordToneOffsets(chord: Chord): Set<number> {\n return new Set(chord.intervals.map((i) => ((i % 12) + 12) % 12));\n}\n\n/**\n * Determine a pitch's harmonic role within a chord.\n *\n * The role is derived from the pitch's interval above the chord root, reduced\n * modulo 12. A major sixth (9) reads as a `sixth` for sixth chords but as a\n * `seventh` for a diminished-seventh chord; ninths and other tensions have no\n * basic role and return null.\n *\n * @param pitch MIDI pitch or bare pitch class.\n * @param chord The chord providing the root reference.\n * @returns The chord-tone role, or null if the pitch has no basic role.\n *\n * @category Chords\n */\nexport function chordToneRole(pitch: number, chord: Chord): ChordToneRole | null {\n const interval = (pitchClass(pitch) - pitchClass(chord.rootPc) + 12) % 12;\n const tones = chordToneOffsets(chord);\n if (interval === 0) {\n return 'root';\n }\n if (interval === 3 || interval === 4) {\n return tones.has(interval) && (interval !== 3 || !tones.has(4)) ? 'third' : null;\n }\n if (interval === 7) {\n return tones.has(7) ? 'fifth' : null;\n }\n if (interval === 6) {\n // A diminished fifth is the chord's fifth only when no perfect fifth is\n // present; alongside a perfect fifth it is a #11 tension, not a fifth.\n return tones.has(6) && !tones.has(7) ? 'fifth' : null;\n }\n if (interval === 8) {\n // An augmented fifth is the chord's fifth only without a perfect fifth;\n // alongside a perfect fifth it is a b13 tension, not a fifth.\n return tones.has(8) && !tones.has(7) ? 'fifth' : null;\n }\n if (interval === 9) {\n const hasHigherSeventh = tones.has(10) || tones.has(11);\n const isDiminishedSeventh = tones.has(3) && tones.has(6) && !hasHigherSeventh;\n if (tones.has(9) && isDiminishedSeventh) {\n return 'seventh';\n }\n if (tones.has(9) && !hasHigherSeventh) {\n return 'sixth';\n }\n return null;\n }\n if (interval === 10 || interval === 11) {\n return tones.has(interval) ? 'seventh' : null;\n }\n return null;\n}\n\n/** Classify a stacked-thirds triad into a chord quality from its interval set. */\nfunction classifyTriad(thirdIc: number, fifthIc: number): ChordQuality {\n if (thirdIc === 4 && fifthIc === 8) {\n return 'aug';\n }\n if (thirdIc === 4 && fifthIc === 6) {\n return 'majb5';\n }\n if (thirdIc === 3 && fifthIc === 6) {\n return 'dim';\n }\n if (thirdIc === 3) {\n return 'min';\n }\n return 'maj';\n}\n\n/** Classify a stacked-thirds seventh chord into a chord quality. */\nfunction classifySeventh(thirdIc: number, fifthIc: number, seventhIc: number): ChordQuality {\n if (thirdIc === 3 && fifthIc === 6 && seventhIc === 9) {\n return 'dim7';\n }\n if (thirdIc === 3 && fifthIc === 6 && seventhIc === 10) {\n return 'm7b5';\n }\n if (thirdIc === 3 && seventhIc === 11) {\n return 'minMaj7';\n }\n if (thirdIc === 3) {\n return 'min7';\n }\n if (fifthIc === 8 && seventhIc === 10) {\n return 'aug7';\n }\n if (fifthIc === 8 && seventhIc === 11) {\n return 'augMaj7';\n }\n if (fifthIc === 6 && seventhIc === 10) {\n return '7b5';\n }\n if (seventhIc === 11) {\n return 'maj7';\n }\n return 'dom7';\n}\n\n/** Whether a classified quality describes precisely the stacked pitch classes. */\nfunction matchesQualityIntervals(quality: ChordQuality, intervals: readonly number[]): boolean {\n const template = QUALITY_INTERVALS[quality];\n return (\n template.length === intervals.length &&\n template.every((interval, index) => interval === intervals[index])\n );\n}\n\n/**\n * Stack scale thirds from a 1-based scale degree into a chord of `size` notes.\n * Degrees beyond the scale length wrap around.\n */\nfunction stackThirds(degree: number, key: KeyScale, size: 3 | 4): Chord {\n // Degree 0 is rejected rather than wrapped onto the last degree: a caller\n // still counting from zero gets an error instead of a plausible chord.\n assertDegree(degree, 'chord degree');\n const tones = scaleTonesInDegreeOrder(key);\n const length = tones.length;\n if (length !== 7) {\n throw new InvalidInputError(\n `diatonic chord stacking requires a heptatonic scale (received ${length} tones)`,\n );\n }\n const idx = (step: number) => (degree - 1 + step) % length;\n const rootPc = tones[idx(0)] ?? pitchClass(key.rootPc);\n const offsets = [0, 2, 4, 6].slice(0, size).map((step) => {\n const pc = tones[idx(step)] ?? rootPc;\n return (((pc - rootPc) % 12) + 12) % 12;\n });\n const thirdIc = offsets[1] ?? 4;\n const fifthIc = offsets[2] ?? 7;\n const quality =\n size === 3\n ? classifyTriad(thirdIc, fifthIc)\n : classifySeventh(thirdIc, fifthIc, offsets[3] ?? 11);\n if (!matchesQualityIntervals(quality, offsets)) {\n throw new InvalidInputError(\n `scale thirds at degree ${degree} produce unsupported intervals [${offsets.join(', ')}]`,\n );\n }\n return { rootPc, quality, intervals: offsets };\n}\n\n/**\n * Build the diatonic triad rooted on a scale degree by stacking scale thirds.\n *\n * Unlike {@link chordFromDegree}, the chord quality is derived from the scale\n * rather than supplied, so degrees yield their scale-correct triads (e.g. a\n * diminished triad on the leading tone of a major key).\n *\n * @param degree 1-based scale degree of the chord root: 1 is the tonic.\n * @param key Key/scale context, as a key name, a key/scale, or a `Key`.\n * @returns The diatonic triad.\n * @throws If the scale is not heptatonic. Stacking thirds means skipping every\n * other degree, which only lands on a triad when there are seven of them, so\n * the pentatonic, blues, whole-tone, octatonic and chromatic scales have no\n * diatonic triad; use {@link chordFromDegree} with an explicit quality there.\n *\n * @example\n * ```ts\n * import { majorKey, diatonicTriad } from '@libraz/libcantus';\n * const tonic = diatonicTriad(1, majorKey(0)); // C major triad (degree 1 of C major)\n * ```\n *\n * @category Chords\n */\nexport function diatonicTriad(degree: number, key: KeyLike): Chord {\n return stackThirds(degree, toKeyScale(key), 3);\n}\n\n/**\n * Build the diatonic seventh chord rooted on a scale degree.\n *\n * @param degree 1-based scale degree of the chord root: 1 is the tonic.\n * @param key Key/scale context, as a key name, a key/scale, or a `Key`.\n * @returns The diatonic seventh chord.\n * @throws If the scale is not heptatonic, for the reason given on\n * {@link diatonicTriad}.\n *\n * @category Chords\n */\nexport function diatonicSeventh(degree: number, key: KeyLike): Chord {\n return stackThirds(degree, toKeyScale(key), 4);\n}\n","/**\n * Spelling: derive letter-name notes for scales and chords from a spelled tonic.\n *\n * The theory core works in pitch classes, which cannot choose between (say) a\n * G# and an Ab. Given a spelled tonic, this module assigns diatonic letters to\n * scale degrees and chord tones so a C major scale spells as C D E F G A B and\n * A harmonic minor spells its seventh as G#.\n *\n * Every entry point takes the spelled tonic *of the key it spells*: a tonic\n * sounding a pitch class other than `key.rootPc` is rejected rather than\n * spelled, because the letters counted from it name no reading of the key.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport type { Note, NoteLike } from '../../core/pitch/index.js';\nimport {\n formatNote,\n diatonicLetterOf as mod7,\n pitchClassOf as mod12,\n naturalPitchClassOf as naturalPc,\n toNoteData,\n} from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { type Chord, chordToneSpellings } from '../chord/index.js';\nimport { isScaleTone, type KeyLike, scaleTonesInDegreeOrder, toKeyScale } from '../scale/index.js';\n\n/**\n * Conventional degree assignment for each chromatic offset above the tonic.\n *\n * The letter is derived from the tonic's letter plus `degreeOffset` (so the\n * spelling follows the key's diatonic letters), and the accidental is recomputed\n * for that letter. The table covers the whole octave, so a pitch class that is\n * not a degree of the scale — a chromatic tone in a heptatonic key, or any tone\n * outside a pentatonic or blues scale — still spells from the tonic's letter\n * rather than falling back to a fixed sharp table.\n *\n * Offsets follow the conventional ascending chromatic spelling: b2, M2, b3, M3,\n * P4, #4, P5, b6, #6, b7, #7. The raised sixth and leading tone keep sharp-side\n * minor keys spelling their raised degrees with the key-correct letter (e.g. F#\n * minor spells its leading tone E#, not F).\n */\nconst CHROMATIC_SPELLING: Record<number, number> = {\n 1: 1, // b2\n 2: 1, // M2\n 3: 2, // b3\n 4: 2, // M3\n 5: 3, // P4\n 6: 3, // #4\n 7: 4, // P5\n 8: 5, // b6\n 9: 5, // #6 (raised sixth of a minor key)\n 10: 6, // b7\n 11: 6, // #7 (raised leading tone of a minor key)\n};\n\n/**\n * Flat-side table for scales that are not heptatonic.\n *\n * Identical to {@link CHROMATIC_SPELLING} except at the tritone, which a\n * flat-side scale spells as a diminished fifth rather than the augmented fourth\n * a lydian mode wants: C blues spells C Eb F Gb G Bb.\n */\nconst CHROMATIC_SPELLING_FLAT: Record<number, number> = {\n ...CHROMATIC_SPELLING,\n 6: 4, // b5\n};\n\n/**\n * Sharp-side counterpart, used for scales that are not heptatonic and lean\n * sharp: C whole-tone spells C D E F# G# A#.\n */\nconst CHROMATIC_SPELLING_SHARP: Record<number, number> = {\n 1: 0, // #1\n 2: 1, // M2\n 3: 1, // #2\n 4: 2, // M3\n 5: 3, // P4\n 6: 3, // #4\n 7: 4, // P5\n 8: 4, // #5\n 9: 5, // M6\n 10: 5, // #6\n 11: 6, // M7\n};\n\n/** The pitch class a spelled note sounds. */\nfunction notePitchClass(note: Note): number {\n return mod12(naturalPc(note.letter) + note.alter);\n}\n\n/**\n * Reject a tonic that does not spell the key's own root.\n *\n * Every letter these functions assign is counted from the tonic's letter, so a\n * tonic naming a different pitch class than `key.rootPc` does not produce\n * another spelling of the same scale: it produces letters no reading of the key\n * supports, and a heptatonic scale that names one letter twice. The class API\n * rejects the pair in its `Key` constructor, so the functions that take the two\n * apart check it here and both surfaces answer the same way.\n *\n * Not part of the package's public surface: it guards the entry points rather\n * than being one.\n *\n * @param tonic The spelled tonic.\n * @param key The key/scale the tonic is meant to spell.\n * @param source The name of the function that received the pair.\n * @throws If the tonic sounds a pitch class other than the key's root.\n */\nexport function assertTonicOf(tonic: Note, key: KeyScale, source: string): void {\n const tonicPc = notePitchClass(tonic);\n const rootPc = mod12(key.rootPc);\n if (tonicPc !== rootPc) {\n const name = formatNote({ letter: mod7(tonic.letter), alter: tonic.alter });\n throw new InvalidInputError(\n `${source}: tonic ${name} sounds pitch class ${tonicPc}, not the key root ${rootPc}; ` +\n 'pass a tonic that spells the key root, or take one from spelledKeyOf',\n );\n }\n}\n\n/** Shortest signed alteration (in [-6, 6]) taking a letter's natural pc to `pc`. */\nfunction alterFor(letter: number, pc: number): number {\n let d = mod12(pc - naturalPc(letter));\n if (d > 6) {\n d -= 12;\n }\n return d;\n}\n\n/** Spell `pc` on the letter `degreeOffset` steps above the tonic's letter. */\nfunction letterFor(tonic: Note, degreeOffset: number | undefined, pc: number): Note | undefined {\n if (degreeOffset === undefined) {\n return undefined;\n }\n const letter = mod7(tonic.letter + degreeOffset);\n return { letter, alter: alterFor(letter, pc) };\n}\n\n/** The lighter-accidental of two candidate spellings; ties go to `preferFlat`. */\nfunction pickSpelling(\n flat: Note | undefined,\n sharp: Note | undefined,\n preferFlat: boolean,\n): Note | undefined {\n if (flat === undefined || sharp === undefined) {\n return flat ?? sharp;\n }\n if (Math.abs(flat.alter) !== Math.abs(sharp.alter)) {\n return Math.abs(flat.alter) < Math.abs(sharp.alter) ? flat : sharp;\n }\n return preferFlat ? flat : sharp;\n}\n\n/**\n * Keep a conventional chromatic degree unless its key-letter spelling costs\n * two or more accidentals than the enharmonic alternative. This preserves Db\n * for C-major bII while avoiding Bbb in Ab major, where A is more readable.\n */\nfunction capChromaticAccidentals(preferred: Note, alternative: Note | undefined): Note {\n return alternative !== undefined && Math.abs(preferred.alter) - Math.abs(alternative.alter) >= 2\n ? alternative\n : preferred;\n}\n\n/**\n * How many letters a spelling has to share out: one per diatonic letter name.\n *\n * A scale with more tones than this cannot give every tone a letter of its own\n * however it is read, so the octatonic and chromatic sets are spelled tone by\n * tone instead.\n */\nconst DIATONIC_LETTER_COUNT = 7;\n\n/** Whether the key's scale is a seven-note (heptatonic) scale. */\nfunction isHeptatonic(key: KeyScale): boolean {\n return scaleTonesInDegreeOrder(key).length === DIATONIC_LETTER_COUNT;\n}\n\n/** Whether the scale has a minor third and no major third — it leans flat. */\nfunction hasMinorThird(key: KeyScale): boolean {\n const tones = scaleTonesInDegreeOrder(key);\n const root = mod12(key.rootPc);\n return tones.includes(mod12(root + 3)) && !tones.includes(mod12(root + 4));\n}\n\n/** Which way a scale leans: a flat tonic and a minor third both mean flats. */\nfunction leansFlat(tonic: Note, key: KeyScale): boolean {\n return tonic.alter < 0 || hasMinorThird(key);\n}\n\n/**\n * The conventional spellings of the tone `offset` semitones above the tonic,\n * the side the scale leans towards first.\n *\n * The two chromatic tables name at most two letters for an offset — the flat\n * side's and the sharp side's — and an offset both tables agree on has only the\n * one. Nothing outside them is offered, so a tone is never pushed onto a letter\n * no reading of the interval supports.\n */\nfunction gappedCandidates(tonic: Note, offset: number, pc: number, preferFlat: boolean): Note[] {\n const flat = letterFor(tonic, CHROMATIC_SPELLING_FLAT[offset], pc);\n const sharp = letterFor(tonic, CHROMATIC_SPELLING_SHARP[offset], pc);\n const preferred = pickSpelling(flat, sharp, preferFlat);\n if (preferred === undefined) {\n return [];\n }\n const alternative = preferred === flat ? sharp : flat;\n return alternative === undefined || alternative.letter === preferred.letter\n ? [preferred]\n : [preferred, alternative];\n}\n\n/**\n * Choose one candidate per tone so that no two share a letter, or undefined\n * when no such choice exists.\n *\n * `reserved` is the letter the tonic has already taken. Every tone offers at\n * most two candidates, so the whole space is small enough to walk: the lightest\n * assignment wins — fewest accidentals overall, and on a tie the one that keeps\n * most tones on the side the scale leans.\n *\n * Because each tone's leading candidate is already its lightest, the\n * tone-by-tone spelling is itself the winner whenever its letters happen to be\n * distinct, which is what keeps a scale of whole tones or wider spelled exactly\n * as it always reads.\n */\nfunction assignDistinctLetters(candidates: Note[][], reserved: number): Note[] | undefined {\n let best: Note[] | undefined;\n let bestAlter = 0;\n let bestDeviations = 0;\n for (let choice = 0; choice < 1 << candidates.length; choice += 1) {\n const picked: Note[] = [];\n let letters = reserved;\n let alter = 0;\n let deviations = 0;\n for (const [index, options] of candidates.entries()) {\n const preferred = options[0];\n const note = options[(choice >> index) & 1] ?? preferred;\n if (note === undefined || (letters & (1 << note.letter)) !== 0) {\n break;\n }\n letters |= 1 << note.letter;\n alter += Math.abs(note.alter);\n deviations += note === preferred ? 0 : 1;\n picked.push(note);\n }\n if (picked.length !== candidates.length) {\n continue;\n }\n if (\n best === undefined ||\n alter < bestAlter ||\n (alter === bestAlter && deviations < bestDeviations)\n ) {\n best = picked;\n bestAlter = alter;\n bestDeviations = deviations;\n }\n }\n return best;\n}\n\n/**\n * Spell every tone of a scale that is not heptatonic, one letter per tone.\n *\n * A heptatonic key spells by degree, which hands every tone its own letter for\n * free. A gapped scale has no such rule, so its tones are spelled together\n * rather than one at a time: the semitone above the tonic of a hemitonic\n * pentatonic is a flattened second (C miyako-bushi spells C Db F G Ab) because\n * the tonic already holds the C, not because a table said so.\n *\n * The answer is undefined — and the caller falls back to spelling tone by tone\n * — when the scale has more tones than there are letters, and when its tones\n * cannot be told apart by letter at all: the blues scale sounds both the fifth\n * and the flattened fifth against a fourth that fixes the letter below them, so\n * one letter is bound to be used twice however the scale is read.\n *\n * @returns A spelling per scale pitch class, or undefined if there is no\n * one-letter-per-tone reading.\n */\nfunction spellGappedScale(tonic: Note, key: KeyScale): Map<number, Note> | undefined {\n const tones = scaleTonesInDegreeOrder(key);\n if (tones.length > DIATONIC_LETTER_COUNT) {\n return undefined;\n }\n const tonicPc = mod12(naturalPc(tonic.letter) + tonic.alter);\n const tonicNote: Note = { letter: mod7(tonic.letter), alter: tonic.alter };\n const preferFlat = leansFlat(tonic, key);\n const rest = tones.filter((pc) => mod12(pc - tonicPc) !== 0);\n const chosen = assignDistinctLetters(\n rest.map((pc) => gappedCandidates(tonic, mod12(pc - tonicPc), pc, preferFlat)),\n 1 << tonicNote.letter,\n );\n if (chosen === undefined) {\n return undefined;\n }\n const spellings = new Map<number, Note>([[tonicPc, tonicNote]]);\n for (const [index, pc] of rest.entries()) {\n const note = chosen[index];\n if (note !== undefined) {\n spellings.set(mod12(pc), note);\n }\n }\n return spellings;\n}\n\n/**\n * Spell a single pitch class from the key alone, with no surrounding context.\n *\n * The tonic always spells as itself. In a heptatonic key each scale degree takes\n * the next letter above the tonic, so the scale spells with one letter per\n * degree. A gapped scale reaches the same one-letter-per-tone spelling through\n * {@link spellGappedScale} wherever its pitch set allows one. Every other pitch\n * class — a chromatic tone, or a tone of a scale whose letters cannot all\n * differ — takes the conventional interval spelling above the tonic's letter,\n * so a flat-side key keeps flat-side names.\n */\nfunction spellByKey(pc: number, tonic: Note, key: KeyScale): Note {\n const tonicPc = mod12(naturalPc(tonic.letter) + tonic.alter);\n const offset = mod12(pc - tonicPc);\n\n // The tonic spells as itself, whatever the scale: a caller that asked for Eb\n // major pentatonic must not be handed back a D#.\n if (offset === 0) {\n return { letter: mod7(tonic.letter), alter: tonic.alter };\n }\n\n const heptatonic = isHeptatonic(key);\n if (heptatonic) {\n const degree = scaleTonesInDegreeOrder(key).indexOf(mod12(pc));\n if (degree >= 0) {\n const letter = mod7(tonic.letter + degree);\n return { letter, alter: alterFor(letter, pc) };\n }\n const degreeOffset = CHROMATIC_SPELLING[offset];\n const preferred = letterFor(tonic, degreeOffset, pc);\n if (preferred !== undefined) {\n const flat = letterFor(tonic, CHROMATIC_SPELLING_FLAT[offset], pc);\n const sharp = letterFor(tonic, CHROMATIC_SPELLING_SHARP[offset], pc);\n // The conventional heptatonic degree is usually the flat-side candidate;\n // use the other side only when it avoids a double (or worse) accidental.\n const alternative =\n flat?.letter === preferred.letter\n ? sharp\n : sharp?.letter === preferred.letter\n ? flat\n : undefined;\n return capChromaticAccidentals(preferred, alternative);\n }\n } else {\n // A scale that is not heptatonic has no letter-per-degree spelling to\n // follow, so its own tones are spelled together, one letter each.\n if (isScaleTone(pc, key)) {\n const assigned = spellGappedScale(tonic, key)?.get(mod12(pc));\n if (assigned !== undefined) {\n return assigned;\n }\n }\n // Anything that joint spelling does not reach — a chromatic tone, or a\n // scale whose tones cannot each hold a letter — takes whichever\n // conventional letter needs the smaller accidental. Ties go to the side the\n // scale itself leans: a flat tonic or a minor third both mean flats (C\n // blues spells Gb and Bb, not F# and A#).\n const [preferred] = gappedCandidates(tonic, offset, pc, leansFlat(tonic, key));\n if (preferred !== undefined) {\n return preferred;\n }\n }\n\n // Unreachable for a pitch class, since both tables cover offsets 1..11 and\n // offset 0 returns above; kept so a non-integral input still names something.\n const belowLetter = mod7([0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6][mod12(pc)] ?? 0);\n return { letter: belowLetter, alter: alterFor(belowLetter, pc) };\n}\n\n/**\n * The musical evidence around a pitch, used to refine an enharmonic choice the\n * key alone cannot make.\n *\n * Every field is optional and every field is only ever advisory: a context that\n * says nothing about the pitch leaves its key spelling untouched.\n *\n * @category Pitch & Intervals\n */\nexport type SpellingContext = {\n /** Root pitch class of the chord sounding under the pitch, when known. */\n chordRoot?: number;\n /** The pitch immediately before this one (MIDI pitch or bare pitch class). */\n previous?: number;\n /** The pitch immediately after this one (MIDI pitch or bare pitch class). */\n next?: number;\n};\n\n/**\n * Letter distance above a chord root for the chord-tone intervals that spell\n * unambiguously: root, minor third, major third, perfect fifth, minor seventh,\n * major seventh.\n *\n * Both thirds sit two letters above the root and both sevenths six, which is\n * what makes the third of a D7 in C major an F# rather than a Gb. The intervals\n * left out are the ones a chord tone does not fix — the tritone, the augmented\n * fifth and the tensions all have two defensible spellings, so they are left to\n * the melodic rules and, failing those, to the key.\n */\nconst CHORD_TONE_LETTER_STEPS: Record<number, number> = {\n 0: 0, // root\n 3: 2, // minor third\n 4: 2, // major third\n 7: 4, // perfect fifth\n 10: 6, // minor seventh\n 11: 6, // major seventh\n};\n\n/** Widest alteration a context-derived spelling may carry: a double accidental. */\nconst MAX_CONTEXT_ALTER = 2;\n\n/** Spell `pc` as a chord tone, on the letter its interval above the root implies. */\nfunction chordToneSpelling(\n pc: number,\n chordRoot: number | undefined,\n tonic: Note,\n key: KeyScale,\n): Note | undefined {\n if (chordRoot === undefined) {\n return undefined;\n }\n const root = spellByKey(mod12(chordRoot), tonic, key);\n return letterFor(root, CHORD_TONE_LETTER_STEPS[mod12(pc - chordRoot)], pc);\n}\n\n/**\n * Spell `pc` relative to a neighbouring pitch, when that neighbour lies `gap`\n * semitones away from it.\n *\n * The neighbour is spelled by the key alone — the pitch being spelled is the\n * ambiguous one, so letting it influence its own evidence would be circular —\n * and `pc` then takes the letter `letterStep` steps from it. A `letterStep` of\n * zero keeps the neighbour's own letter, which spells `pc` as that note\n * inflected by an accidental. Neighbours compare as pitch classes, so a MIDI\n * pitch and a bare pitch class both work.\n */\nfunction neighbourSpelling(\n neighbour: number | undefined,\n gap: number,\n letterStep: number,\n pc: number,\n tonic: Note,\n key: KeyScale,\n): Note | undefined {\n if (neighbour === undefined || mod12(neighbour - pc) !== mod12(gap)) {\n return undefined;\n }\n return letterFor(spellByKey(mod12(neighbour), tonic, key), letterStep, pc);\n}\n\n/**\n * Refine a key spelling with the surrounding evidence, in rule priority order.\n *\n * A scale tone and the tonic itself are never touched: the key already spells\n * them, and a context must not turn the third of the scale into something else.\n * For anything chromatic the rules are tried in order and the first spelling\n * that stays within a double accidental wins; when none does, the key spelling\n * stands.\n */\nfunction refineByContext(\n pc: number,\n tonic: Note,\n key: KeyScale,\n context: SpellingContext,\n keySpelling: Note,\n): Note {\n const tonicPc = mod12(naturalPc(tonic.letter) + tonic.alter);\n if (mod12(pc) === tonicPc || isScaleTone(pc, key)) {\n return keySpelling;\n }\n const { chordRoot, previous, next } = context;\n const candidates = [\n // The sounding chord names the pitch by its interval above the root.\n chordToneSpelling(pc, chordRoot, tonic, key),\n // A semitone below what follows: the pitch is its leading tone, a letter down.\n neighbourSpelling(next, 1, -1, pc, tonic, key),\n // A semitone above what preceded: the pitch is that note inflected upward,\n // so it keeps its letter and a chromatic ascent spells with sharps.\n neighbourSpelling(previous, -1, 0, pc, tonic, key),\n // A semitone above what follows: the pitch leans on it from a letter up,\n // so a falling line spells with flats.\n neighbourSpelling(next, -1, 1, pc, tonic, key),\n // Failing that, a semitone below what preceded: that note inflected downward,\n // again on its own letter.\n neighbourSpelling(previous, 1, 0, pc, tonic, key),\n ];\n for (const candidate of candidates) {\n if (candidate !== undefined && Math.abs(candidate.alter) <= MAX_CONTEXT_ALTER) {\n return candidate;\n }\n }\n return keySpelling;\n}\n\n/**\n * Spell a single pitch class relative to a spelled tonic and key, optionally\n * refined by the music around it.\n *\n * Without a context the key decides alone: the tonic spells as itself, each\n * degree of a heptatonic key takes the next letter above the tonic, a gapped\n * scale gives each of its own tones a letter wherever its pitch set allows one,\n * and every other pitch class takes the conventional interval spelling above\n * the tonic's letter, so a flat-side key keeps flat-side names.\n *\n * A context only ever refines a pitch class the key leaves ambiguous — one that\n * is not a tone of `key`. Scale tones, and the tonic, keep their key spelling\n * whatever the context says.\n *\n * The melodic rules read the two neighbours differently, because a chromatic\n * note is an inflection of the note it came from but a leading tone into the\n * note it goes to: `next` moves the letter, `previous` keeps it. For a chromatic\n * pitch class the evidence is weighed in this order, and the first spelling\n * within a double accidental wins:\n *\n * 1. The sounding chord, when `chordRoot` is given and the pitch is an\n * unambiguous chord tone above it (root, third, fifth or seventh) — the third\n * of a secondary dominant D7 in C major spells F#, not Gb.\n * 2. Resolution: `next` a semitone above makes the pitch a leading tone into it,\n * one letter below — the C# rising to D in D minor.\n * 3. Approach: `previous` a semitone below inflects that note upward, keeping\n * its letter, so a chromatic ascent spells with sharps — F F# G in C major.\n * 4. Descent: `next` a semitone below puts the pitch one letter above it, so a\n * falling chromatic line spells with flats — A Ab G in C major. Failing that,\n * `previous` a semitone above inflects that note downward on its own letter,\n * which spells the same line from the note before instead.\n *\n * If no rule applies, or every candidate would need more than a double\n * accidental, the key spelling stands.\n *\n * @param pc The pitch class to spell.\n * @param tonic The spelled tonic (its letter anchors the spelling), as a note\n * name, a MIDI number, note data, or a `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`.\n * @param context Optional surrounding evidence; an absent or empty context\n * spells exactly as the key alone does.\n * @returns The spelled note (without octave).\n * @throws If `tonic` does not sound the key's root pitch class.\n * @example\n * ```ts\n * import { spellPitchClass, formatNote, parseNote, majorKey } from '@libraz/libcantus';\n * const c = parseNote('C');\n * formatNote(spellPitchClass(6, c, majorKey(0))); // 'F#'\n * // The third of D7, the secondary dominant of G.\n * formatNote(spellPitchClass(6, 'C', 'C major', { chordRoot: 2 })); // 'F#'\n * // A chromatic passing tone falling from G to F.\n * formatNote(spellPitchClass(6, c, majorKey(0), { previous: 7, next: 5 })); // 'Gb'\n * ```\n * @category Pitch & Intervals\n */\nexport function spellPitchClass(\n pc: number,\n tonic: NoteLike,\n key: KeyLike,\n context?: SpellingContext,\n): Note {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n assertTonicOf(note, scale, 'spellPitchClass');\n return spelledPitchClass(pc, note, scale, context);\n}\n\n/** Spell a pitch class from an already-resolved tonic and key. */\nfunction spelledPitchClass(\n pc: number,\n tonic: Note,\n key: KeyScale,\n context?: SpellingContext,\n): Note {\n const keySpelling = spellByKey(pc, tonic, key);\n return context === undefined\n ? keySpelling\n : refineByContext(pc, tonic, key, context, keySpelling);\n}\n\n/**\n * Spell every pitch class of a scale, in ascending scale-degree order.\n *\n * Correct for heptatonic scales (each degree gets the next letter). A gapped\n * scale gives each of its tones a letter of its own wherever its pitch set\n * allows one — C miyako-bushi spells C Db F G Ab — and is spelled tone by tone,\n * on the accidental side the scale leans towards, where it does not: the blues\n * scale has to name one letter twice however it is read.\n *\n * @param tonic The spelled tonic, as a note name, a MIDI number, note data, or a\n * `Note`.\n * @param key The key/scale. This one stays a plain key/scale: the key-spelling\n * search reads scales through it that no key name describes.\n * @returns Spelled notes, one per scale degree.\n * @throws If `tonic` does not sound the key's root pitch class.\n * @example\n * ```ts\n * import { spellScale, noteNames, parseNote, majorKey } from '@libraz/libcantus';\n * noteNames(spellScale(parseNote('C'), majorKey(0)));\n * // ['C', 'D', 'E', 'F', 'G', 'A', 'B']\n * ```\n * @category Pitch & Intervals\n */\nexport function spellScale(tonic: NoteLike, key: KeyScale): Note[] {\n const note = toNoteData(tonic);\n const scale = key;\n assertTonicOf(note, scale, 'spellScale');\n return scaleTonesInDegreeOrder(scale).map((pc) => spelledPitchClass(pc, note, scale));\n}\n\n/**\n * Spell an arbitrary list of pitch classes relative to a key.\n *\n * @param pcs The pitch classes.\n * @param tonic The spelled tonic, as a note name, a MIDI number, note data, or a\n * `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`.\n * @returns Spelled notes, in input order.\n * @throws If `tonic` does not sound the key's root pitch class.\n * @category Pitch & Intervals\n */\nexport function spellPitchClasses(pcs: number[], tonic: NoteLike, key: KeyLike): Note[] {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n assertTonicOf(note, scale, 'spellPitchClasses');\n return pcs.map((pc) => spelledPitchClass(pc, note, scale));\n}\n\n/**\n * Diatonic letter distance implied by a chord interval.\n *\n * Chord intervals are not merely pitch-class distances: 6 is a diminished\n * fifth in the basic chord templates while 18 is an augmented eleventh, and 9\n * is a diminished seventh in `dim7` but a sixth elsewhere. Keeping that\n * distinction is what prevents functional chord tones from being respelled by\n * their nearest key pitch (for example G# becoming Ab in E7).\n */\nfunction chordLetterOffset(interval: number, chord: Chord): number {\n switch (interval) {\n case 0:\n case 12:\n return 0;\n case 1:\n case 2:\n case 13:\n case 14:\n return 1;\n case 3:\n case 4:\n case 16:\n return 2;\n case 15:\n // A raised ninth over a chord that already has a third is a ninth, not a\n // second third: C7#9 spells D#, not a duplicate E.\n return chord.intervals.some((i) => i === 3 || i === 4) ? 1 : 2;\n case 5:\n case 17:\n case 18:\n return 3;\n case 6:\n case 7:\n case 8:\n case 19:\n return 4;\n case 9:\n return chord.quality === 'dim7' ? 6 : 5;\n case 20:\n case 21:\n return 5;\n case 10:\n case 11:\n case 22:\n case 23:\n return 6;\n default: {\n // Unknown/custom interval data gets the nearest conventional diatonic\n // distance. Public builders use the explicit cases above.\n const octaves = Math.floor(Math.max(0, interval) / 12);\n const simple = mod12(interval);\n const simpleOffset = [0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6][simple] ?? 0;\n return octaves * 7 + simpleOffset;\n }\n }\n}\n\n/**\n * The letter distance above the root each of a chord's own tone spellings\n * implies, or undefined when the chord names none this function can trust.\n *\n * This is the one route by which a chord can spell a tone {@link chordLetterOffset}\n * cannot: the augmented sixth over the lowered submediant is ten semitones like\n * a minor seventh, but five letters above its root rather than six, and no\n * rotation of the chord makes that a stack of thirds. Distances are read out of\n * the hint rather than letters, and the distances are measured from the hint's\n * own root tone, so the tones follow whichever spelling of the root is in\n * force: the German sixth spells Ab C Eb F# from an Ab root and G# B# D# E##\n * from a G# one.\n */\nfunction hintedLetterOffsets(chord: Chord): number[] | undefined {\n const hints = chordToneSpellings(chord);\n const rootHint = hints?.[chord.intervals.findIndex((interval) => mod12(interval) === 0)];\n if (hints === undefined || rootHint === undefined) {\n return undefined;\n }\n return hints.map((hint) => mod7(hint.letter - rootHint.letter));\n}\n\n/**\n * Spell chord tones from an already chosen root spelling.\n *\n * This is public for callers that already know the desired root spelling;\n * {@link spellChord} instead derives that spelling from the chord and key.\n *\n * Letters follow the chord's own tone spellings when it carries a usable set,\n * and otherwise the diatonic distance each interval implies in a stack of\n * thirds. Accidentals are always recomputed from the sounding pitch class, so\n * the tones name exactly the chord's own pitch classes either way.\n *\n * @param chord The chord whose tones are spelled.\n * @param root The chosen root spelling, as a note name, a MIDI number, note\n * data, or a `Note`.\n */\nexport function spellChordFromRoot(chord: Chord, root: NoteLike): Note[] {\n return chordTonesFromRoot(chord, toNoteData(root));\n}\n\n/** Spell chord tones from an already-resolved root spelling. */\nfunction chordTonesFromRoot(chord: Chord, root: Note): Note[] {\n if (mod12(naturalPc(root.letter) + root.alter) !== mod12(chord.rootPc)) {\n throw new InvalidInputError('chord root spelling must match chord.rootPc');\n }\n const hinted = hintedLetterOffsets(chord);\n return chord.intervals.map((interval, index) => {\n const letter = mod7(root.letter + (hinted?.[index] ?? chordLetterOffset(interval, chord)));\n const pc = mod12(chord.rootPc + interval);\n return { letter, alter: alterFor(letter, pc) };\n });\n}\n\n/**\n * Spell a chord's tones, in the chord's own (tertian) order, relative to a key.\n *\n * Diatonic chords spell exactly (e.g. G7 in C major -> G B D F). Chromatic chord\n * tones take their conventional spelling; enharmonically ambiguous altered\n * tensions may be spelled by the general convention rather than by chord\n * function.\n *\n * A chord carrying its own tone spellings is spelled by them, since a chord\n * whose letters do not follow from stacking thirds can say so no other way:\n * that is what keeps the German sixth of C major on its F#, the note that\n * resolves outward to the dominant, rather than the Gb a dominant seventh\n * would put there.\n *\n * @param chord The chord.\n * @param tonic The spelled tonic of the key, as a note name, a MIDI number,\n * note data, or a `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`.\n * @returns Spelled chord tones, root first.\n * @throws If `tonic` does not sound the key's root pitch class.\n * @example\n * ```ts\n * import { spellChord, noteNames, parseNote, majorKey, makeChord } from '@libraz/libcantus';\n * // G7 in C major\n * noteNames(spellChord(makeChord(7, 'dom7'), parseNote('C'), majorKey(0)));\n * // ['G', 'B', 'D', 'F']\n * ```\n * @category Pitch & Intervals\n */\nexport function spellChord(chord: Chord, tonic: NoteLike, key: KeyLike): Note[] {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n assertTonicOf(note, scale, 'spellChord');\n return chordTonesFromRoot(chord, chordRootSpelling(chord, note, scale));\n}\n\n/**\n * The root spelling to use for a chord: the chord's own hint when it still\n * matches the root pitch class, otherwise the key's spelling of that pitch\n * class.\n *\n * Honouring the hint is what keeps a chord's spelled tones agreeing with the\n * symbol it renders as — `parseChordSymbol('C#7')` must spell C# E# G# B even\n * when analysed in a flat-side key.\n */\nfunction chordRootSpelling(chord: Chord, tonic: Note, key: KeyScale): Note {\n const hint = chord.rootSpelling;\n if (hint !== undefined && mod12(naturalPc(hint.letter) + hint.alter) === mod12(chord.rootPc)) {\n return { letter: mod7(hint.letter), alter: hint.alter };\n }\n return spelledPitchClass(chord.rootPc, tonic, key);\n}\n\n/**\n * Spell a MIDI pitch relative to a key, keeping its octave.\n *\n * The octave-less sibling of this function, {@link spellPitchClass}, is enough\n * for chord and scale spelling; this one is for naming actual sounding pitches\n * (a generated line, an imported track) without losing the register.\n *\n * The octave is the one that reproduces `pitch` from the chosen spelling, so a\n * context that changes the letter changes the octave with it: MIDI 60 spelled\n * B# is B#3, not B#4.\n *\n * @param pitch The MIDI pitch; rounded to the nearest integer.\n * @param tonic The spelled tonic of the key, as a note name, a MIDI number,\n * note data, or a `Note`.\n * @param key The key/scale, as a key name, a key/scale, or a `Key`.\n * @param context Optional surrounding evidence, weighed exactly as\n * {@link spellPitchClass} weighs it.\n * @returns The spelled note, carrying the octave that reproduces `pitch`.\n * @throws If `tonic` does not sound the key's root pitch class.\n * @example\n * ```ts\n * import { spellPitch, formatNote, parseNote, majorKey } from '@libraz/libcantus';\n * formatNote(spellPitch(70, parseNote('Eb'), majorKey(3))); // 'Bb4'\n * ```\n * @category Pitch & Intervals\n */\nexport function spellPitch(\n pitch: number,\n tonic: NoteLike,\n key: KeyLike,\n context?: SpellingContext,\n): Note {\n const note = toNoteData(tonic);\n const scale = toKeyScale(key);\n assertTonicOf(note, scale, 'spellPitch');\n const rounded = Math.round(pitch);\n const spelled = spelledPitchClass(mod12(rounded), note, scale, context);\n const octave = (rounded - naturalPc(spelled.letter) - spelled.alter) / 12 - 1;\n return { letter: spelled.letter, alter: spelled.alter, octave };\n}\n\n/**\n * Convenience: render spelled notes as letter-name strings.\n *\n * @param notes The notes, each a note name, a MIDI number, note data, or a\n * `Note`.\n * @returns Their formatted names.\n * @category Pitch & Intervals\n */\nexport function noteNames(notes: readonly NoteLike[]): string[] {\n // Wrapped rather than passed by reference: `map` supplies an index that\n // `formatNote`'s options parameter would read as a naming system.\n return notes.map((note) => formatNote(toNoteData(note)));\n}\n","/**\n * Shared interval reading for the counterpoint rules.\n *\n * The predicates, the species checker and the independence metric all need the\n * same two conversions — a pitch argument to a sounding pitch, and a spelled\n * interval to its consonance class — and importing them from one another would\n * make the module graph circular.\n */\n\nimport type { ConsonanceClass } from '../../core/interval/index.js';\nimport { ConsonanceClass as Consonance } from '../../core/interval/index.js';\nimport type { Note, SpelledInterval } from '../../core/pitch/index.js';\nimport { noteToMidi } from '../../core/pitch/index.js';\n\n/** The sounding pitch of a predicate argument, whichever form it arrived in. */\nexport function pitchOf(value: number | Note): number {\n return typeof value === 'number' ? value : noteToMidi(value);\n}\n\n/** Reduce a compound diatonic number to its simple form in [1, 7]. */\nexport function simpleIntervalNumber(numberValue: number): number {\n return ((Math.abs(numberValue) - 1) % 7) + 1;\n}\n\n/**\n * Classify a spelled interval by consonance, the way a counterpoint exercise\n * reads it.\n *\n * The spelling decides, not the semitone count: a diminished fourth is a\n * dissonance though it sounds like a major third, and an augmented fifth is a\n * dissonance though it sounds like a minor sixth. Only the perfect unison, fifth\n * and octave and the major and minor thirds and sixths are consonant. The\n * perfect fourth is context-dependent exactly as in {@link classifyInterval}.\n *\n * @param interval The spelled interval between the two voices.\n * @param twoVoice When true, the perfect fourth counts as a dissonance.\n * @returns The counterpoint quality of the interval.\n * @example\n * ```ts\n * import { classifySpelledInterval, ConsonanceClass, parseInterval } from '@libraz/libcantus';\n * classifySpelledInterval(parseInterval('d4')); // ConsonanceClass.Dissonance\n * classifySpelledInterval(parseInterval('M3')); // ConsonanceClass.ImperfectConsonance\n * ```\n * @category Voicing & Counterpoint\n */\nexport function classifySpelledInterval(\n interval: SpelledInterval,\n twoVoice = true,\n): ConsonanceClass {\n const simple = simpleIntervalNumber(interval.number);\n const quality = interval.quality;\n if (quality === 'P') {\n if (simple === 1 || simple === 5) {\n return Consonance.PerfectConsonance;\n }\n if (simple === 4) {\n return twoVoice ? Consonance.Dissonance : Consonance.ImperfectConsonance;\n }\n return Consonance.Dissonance;\n }\n if ((quality === 'M' || quality === 'm') && (simple === 3 || simple === 6)) {\n return Consonance.ImperfectConsonance;\n }\n return Consonance.Dissonance;\n}\n","/**\n * Voice independence as a measurement rather than a verdict.\n *\n * The species rules answer \"is this allowed\", which is the wrong question of a\n * pop arrangement: a harmony line in parallel thirds, a power-chord riff and a\n * pedal point all break them and all are the intended sound. This module\n * reports how independent two lines actually are and leaves the judgement to\n * the caller.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport { ConsonanceClass } from '../../core/interval/index.js';\nimport type { Note } from '../../core/pitch/index.js';\nimport { spelledInterval } from '../../core/pitch/index.js';\nimport { classifySpelledInterval, pitchOf, simpleIntervalNumber } from './internal.js';\n\n/**\n * How two lines move against each other, and how far apart they sit.\n *\n * @category Voicing & Counterpoint\n */\nexport type VoiceIndependenceReport = {\n /**\n * Share of the moves that fall into each motion type, summing to 1. A slot\n * pair in which neither voice moves is not a move and is left out entirely,\n * so an accompaniment that mostly sits still can still report mostly contrary\n * motion. All four are 0 when nothing moves.\n */\n motion: {\n /** The voices move in opposite directions. */\n contrary: number;\n /** One voice holds while the other moves. */\n oblique: number;\n /** Both rise or both fall, and the interval between them changes. */\n similar: number;\n /** Both rise or both fall, keeping the same interval. */\n parallel: number;\n };\n /**\n * Share of the slots where the lead holds or rests on which the counter\n * attacks: 1 when the counter fills every gap the lead leaves, 0 when it moves\n * only where the lead does. It is 0 when the lead never holds or rests, since\n * there was nothing to complement.\n */\n rhythmicComplementarity: number;\n /** Distance between the two lines, in semitones, over the slots where both sound. */\n separation: {\n /** Mean distance; 0 when the two lines never sound together. */\n mean: number;\n /** Closest approach; 0 when the two lines never sound together. */\n min: number;\n };\n /**\n * Slots at which the two lines sit in the opposite order to the one they keep\n * overall. A unison is not a crossing.\n */\n crossings: number;\n /** Longest run of consecutive slots sounding a perfect consonance. */\n longestPerfectRun: number;\n /** Slots at which both lines sound, the denominator of `separation`. */\n sounding: number;\n};\n\n/**\n * Options controlling {@link voiceIndependence}.\n *\n * @category Voicing & Counterpoint\n */\nexport type VoiceIndependenceOptions = {\n /**\n * Which slots are fresh attacks in the lead. Without it a slot counts as an\n * attack when its note differs from the previous slot's, which reads a\n * deliberately repeated note as a sustain.\n */\n leadAttacks?: readonly boolean[];\n /** Which slots are fresh attacks in the counter; read as `leadAttacks` is. */\n counterAttacks?: readonly boolean[];\n /**\n * Count the perfect fourth among the perfect consonances when measuring\n * `longestPerfectRun`. Useful for quartal writing, where the fourth is the\n * interval being sustained.\n *\n * @defaultValue false\n */\n countFourths?: boolean;\n};\n\n/** One slot of both lines, resolved to what the metrics read off it. */\ntype Slot = {\n lead: Note | null;\n counter: Note | null;\n leadPitch: number | null;\n counterPitch: number | null;\n};\n\n/** Whether a slot has both voices sounding. */\nfunction bothSound(slot: Slot): slot is Slot & { lead: Note; counter: Note } {\n return slot.lead !== null && slot.counter !== null;\n}\n\n/** Whether a voice attacks at a slot, by the caller's flags or by note change. */\nfunction attacksAt(\n index: number,\n notes: readonly (Note | null)[],\n flags: readonly boolean[] | undefined,\n): boolean {\n if (flags !== undefined) {\n return flags[index] === true;\n }\n const current = notes[index];\n if (current === undefined || current === null) {\n return false;\n }\n const previous = index > 0 ? notes[index - 1] : undefined;\n if (previous === undefined || previous === null) {\n return true;\n }\n return (\n previous.letter !== current.letter ||\n previous.alter !== current.alter ||\n previous.octave !== current.octave\n );\n}\n\n/** The motion the two voices make between two adjacent sounding slots. */\nfunction motionBetween(\n from: Slot & { lead: Note; counter: Note },\n to: Slot & { lead: Note; counter: Note },\n): 'contrary' | 'oblique' | 'similar' | 'parallel' | null {\n const leadMove = pitchOf(to.lead) - pitchOf(from.lead);\n const counterMove = pitchOf(to.counter) - pitchOf(from.counter);\n if (leadMove === 0 && counterMove === 0) {\n return null;\n }\n if (leadMove === 0 || counterMove === 0) {\n return 'oblique';\n }\n if (leadMove > 0 !== counterMove > 0) {\n return 'contrary';\n }\n const before = simpleIntervalNumber(spelledInterval(from.counter, from.lead).number);\n const after = simpleIntervalNumber(spelledInterval(to.counter, to.lead).number);\n return before === after ? 'parallel' : 'similar';\n}\n\n/**\n * Measure how independent a counter line is from the lead it accompanies.\n *\n * Both lines are index-aligned samples: one slot per beat (or whatever grid the\n * caller works on), `null` for a rest, and a repeated note for a sustain. That\n * is what makes the rhythmic reading possible without a duration model, and\n * what makes a re-attacked repeated note look like a sustain unless\n * `leadAttacks` / `counterAttacks` say otherwise. Notes must carry octaves,\n * since separation and motion are measured between sounding pitches.\n *\n * Nothing here is a verdict. Parallel thirds report as parallel motion with a\n * small separation, a pedal point as oblique motion, a power-chord line as a\n * long perfect run — all facts about the texture, not faults in it.\n *\n * @param lead The leading line, one entry per slot.\n * @param counter The accompanying line, aligned slot for slot with `lead`.\n * @param opts Attack flags and the treatment of the fourth.\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, if an attack array is supplied at\n * another length than the lines, or if a note carries no octave.\n * @example\n * ```ts\n * import { parseNote, voiceIndependence } from '@libraz/libcantus';\n * const lead = ['C5', 'D5', 'E5'].map((n) => parseNote(n));\n * const counter = ['E4', 'F4', 'G4'].map((n) => parseNote(n));\n * voiceIndependence(lead, counter).motion.parallel; // 1 — a harmony line in sixths\n * ```\n * @category Voicing & Counterpoint\n */\nexport function voiceIndependence(\n lead: readonly (Note | null)[],\n counter: readonly (Note | null)[],\n opts?: VoiceIndependenceOptions,\n): VoiceIndependenceReport {\n if (lead.length !== counter.length) {\n throw new InvalidInputError(\n `voiceIndependence needs the two lines aligned slot for slot; received ${lead.length} and ${counter.length}`,\n );\n }\n // The attack arrays are read slot by slot alongside the lines, so one of the\n // wrong length is not a shorter reading of the same texture but a silently\n // wrong one: every slot past its end would count as a sustain.\n for (const [name, flags] of [\n ['leadAttacks', opts?.leadAttacks],\n ['counterAttacks', opts?.counterAttacks],\n ] as const) {\n if (flags !== undefined && flags.length !== lead.length) {\n throw new InvalidInputError(\n `voiceIndependence needs ${name} aligned slot for slot with the lines; received ${flags.length} and ${lead.length}`,\n );\n }\n }\n const slots: Slot[] = lead.map((leadNote, index) => {\n const counterNote = counter[index] ?? null;\n return {\n lead: leadNote,\n counter: counterNote,\n leadPitch: leadNote === null ? null : pitchOf(leadNote),\n counterPitch: counterNote === null ? null : pitchOf(counterNote),\n };\n });\n\n const counts = { contrary: 0, oblique: 0, similar: 0, parallel: 0 };\n let moves = 0;\n for (let index = 1; index < slots.length; index += 1) {\n const from = slots[index - 1];\n const to = slots[index];\n if (from === undefined || to === undefined || !bothSound(from) || !bothSound(to)) {\n continue;\n }\n const motion = motionBetween(from, to);\n if (motion === null) {\n continue;\n }\n counts[motion] += 1;\n moves += 1;\n }\n\n let filled = 0;\n let gaps = 0;\n for (let index = 0; index < slots.length; index += 1) {\n const leadHolds = !attacksAt(index, lead, opts?.leadAttacks);\n if (!leadHolds) {\n continue;\n }\n gaps += 1;\n if (attacksAt(index, counter, opts?.counterAttacks)) {\n filled += 1;\n }\n }\n\n let sounding = 0;\n let totalGap = 0;\n let minGap = Number.POSITIVE_INFINITY;\n let orderSum = 0;\n let perfectRun = 0;\n let longestPerfectRun = 0;\n for (const slot of slots) {\n if (slot.leadPitch === null || slot.counterPitch === null) {\n perfectRun = 0;\n continue;\n }\n sounding += 1;\n const gap = slot.leadPitch - slot.counterPitch;\n totalGap += Math.abs(gap);\n minGap = Math.min(minGap, Math.abs(gap));\n orderSum += Math.sign(gap);\n const interval = spelledInterval(slot.counter as Note, slot.lead as Note);\n const perfect =\n classifySpelledInterval(interval) === ConsonanceClass.PerfectConsonance ||\n ((opts?.countFourths ?? false) &&\n interval.quality === 'P' &&\n simpleIntervalNumber(interval.number) === 4);\n perfectRun = perfect ? perfectRun + 1 : 0;\n longestPerfectRun = Math.max(longestPerfectRun, perfectRun);\n }\n\n // The prevailing order is whichever way round the two lines sit most of the\n // time; a line written above the lead is as ordinary as one written below, and\n // only the departures from its own habit count as crossings.\n const leadAbove = orderSum >= 0;\n let crossings = 0;\n for (const slot of slots) {\n if (slot.leadPitch === null || slot.counterPitch === null) {\n continue;\n }\n const gap = slot.leadPitch - slot.counterPitch;\n if (gap !== 0 && gap > 0 !== leadAbove) {\n crossings += 1;\n }\n }\n\n return {\n motion: {\n contrary: moves === 0 ? 0 : counts.contrary / moves,\n oblique: moves === 0 ? 0 : counts.oblique / moves,\n similar: moves === 0 ? 0 : counts.similar / moves,\n parallel: moves === 0 ? 0 : counts.parallel / moves,\n },\n rhythmicComplementarity: gaps === 0 ? 0 : filled / gaps,\n separation: {\n mean: sounding === 0 ? 0 : totalGap / sounding,\n min: sounding === 0 ? 0 : minGap,\n },\n crossings,\n longestPerfectRun,\n sounding,\n };\n}\n","/**\n * Counterpoint predicates: each judges one pair of voices at one moment.\n *\n * Every predicate reads spelled notes, because spelling is what decides two of\n * these rules. An augmented second and a minor third span the same three\n * semitones, and a diminished fourth sounds like a major third; the first is a\n * forbidden melodic leap and the second a vertical dissonance, and neither can\n * be seen in a MIDI integer. {@link createsVerticalDissonance} and\n * {@link isForbiddenMelodicLeap} therefore answer the spelled question when\n * given spelled notes, and the sounding question — all a bare pitch can support\n * — when given integers. {@link isAugmentedMelodicInterval} asks a question that\n * has no sounding half at all, so its integer form answers false rather than\n * refusing the call: every predicate here takes either form, and the ones that\n * need spelling say so by degrading.\n *\n * The remaining rules compare registers or perfect classes, which spelling\n * cannot change: a parallel fifth is a parallel fifth however the two voices are\n * written. Those keep their integer form as an equal partner.\n */\n\nimport { ConsonanceClass, isConsonantInterval } from '../../core/interval/index.js';\nimport type { Note, SpelledInterval } from '../../core/pitch/index.js';\nimport { pitchClassOf as pitchClass, spelledInterval } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { classifySpelledInterval, pitchOf, simpleIntervalNumber } from './internal.js';\n\n/**\n * Whether an upper voice has crossed below a lower voice.\n *\n * @param upper The nominally higher voice.\n * @param lower The nominally lower voice.\n * @returns True when the upper voice sits below the lower voice.\n * @category Voicing & Counterpoint\n */\nexport function createsVoiceCrossing(upper: Note, lower: Note): boolean;\nexport function createsVoiceCrossing(upper: number, lower: number): boolean;\nexport function createsVoiceCrossing(upper: number | Note, lower: number | Note): boolean {\n return pitchOf(upper) < pitchOf(lower);\n}\n\n/**\n * Whether two simultaneous voices form a dissonance.\n *\n * Given spelled notes the spelling decides, so a diminished fourth reads as the\n * dissonance it is. Given MIDI integers only the sounding interval is available,\n * which cannot tell that fourth from a major third; use the spelled form\n * wherever the exercise is written in notes.\n *\n * @param a First voice.\n * @param b Second voice.\n * @param twoVoice When true, the perfect fourth counts as dissonant.\n * @returns True when the vertical interval is dissonant.\n * @example\n * ```ts\n * import { createsVerticalDissonance, parseNote } from '@libraz/libcantus';\n * createsVerticalDissonance(parseNote('Fb4'), parseNote('C4'), true); // true — d4\n * createsVerticalDissonance(64, 60, true); // false — the same pitches read as a major third\n * ```\n * @category Voicing & Counterpoint\n */\nexport function createsVerticalDissonance(a: Note, b: Note, twoVoice: boolean): boolean;\nexport function createsVerticalDissonance(a: number, b: number, twoVoice: boolean): boolean;\nexport function createsVerticalDissonance(\n a: number | Note,\n b: number | Note,\n twoVoice: boolean,\n): boolean {\n if (typeof a !== 'number' && typeof b !== 'number') {\n // The interval number and quality are the same read either way round, so the\n // argument order only decides a sign the classification never looks at.\n return classifySpelledInterval(spelledInterval(a, b), twoVoice) === ConsonanceClass.Dissonance;\n }\n return !isConsonantInterval(pitchOf(a) - pitchOf(b), twoVoice);\n}\n\n/**\n * Whether a melodic move is a forbidden leap.\n *\n * Forbidden are the seventh in either quality, any leap wider than an octave,\n * and — where the spelling is known — every augmented and diminished interval,\n * the augmented second of the harmonic minor among them. Given MIDI integers the\n * tritone is still caught, since it is forbidden under either spelling, but an\n * augmented second is indistinguishable from a minor third and passes.\n *\n * The raw distance is used for the octave check, so a compound leap is not\n * reduced to its simple class first.\n *\n * @param prev Starting note.\n * @param cur Ending note.\n * @returns True when the leap is forbidden in strict counterpoint.\n * @example\n * ```ts\n * import { isForbiddenMelodicLeap, parseNote } from '@libraz/libcantus';\n * isForbiddenMelodicLeap(parseNote('Ab4'), parseNote('B4')); // true — augmented second\n * isForbiddenMelodicLeap(68, 71); // false — the same pitches read as a minor third\n * ```\n * @category Voicing & Counterpoint\n */\nexport function isForbiddenMelodicLeap(prev: Note, cur: Note): boolean;\nexport function isForbiddenMelodicLeap(prev: number, cur: number): boolean;\nexport function isForbiddenMelodicLeap(prev: number | Note, cur: number | Note): boolean {\n if (typeof prev !== 'number' && typeof cur !== 'number') {\n const interval = spelledInterval(prev, cur);\n if (Math.abs(interval.semitones) > 12 || interval.number > 8) {\n return true;\n }\n if (simpleIntervalNumber(interval.number) === 7) {\n return true;\n }\n return isAlteredInterval(interval);\n }\n const semis = Math.abs(pitchOf(cur) - pitchOf(prev));\n return semis === 6 || semis === 10 || semis === 11 || semis > 12;\n}\n\n/** Whether a spelled interval is augmented or diminished rather than plain. */\nfunction isAlteredInterval(interval: SpelledInterval): boolean {\n // The altered unison is left out: a chromatic inflection of the note a voice\n // already holds is ordinary voice leading, not a leap at all.\n return interval.number >= 2 && (interval.quality.startsWith('A') || interval.quality[0] === 'd');\n}\n\n/**\n * Whether a voice moves by an augmented interval — the melodic step no\n * sixteenth-century line takes, and the one a MIDI integer cannot show.\n *\n * The augmented unison is excluded: raising or lowering the note a voice already\n * holds is a chromatic inflection, not a leap.\n *\n * Every augmented interval sounds like a plain one — the augmented second like a\n * minor third, the augmented fourth like a diminished fifth — so MIDI integers\n * carry nothing this rule can read, and the numeric form always answers false.\n * It exists so a host can run the whole predicate set over a bare pitch pair;\n * take the spelled form wherever the exercise is written in notes.\n *\n * @param prev Starting note.\n * @param cur Ending note.\n * @returns True when the move spans an augmented second or wider.\n * @example\n * ```ts\n * import { isAugmentedMelodicInterval, parseNote } from '@libraz/libcantus';\n * isAugmentedMelodicInterval(parseNote('Ab4'), parseNote('B4')); // true — A2 in C minor\n * isAugmentedMelodicInterval(parseNote('A4'), parseNote('C5')); // false — m3\n * isAugmentedMelodicInterval(68, 71); // false — the same pitches carry no spelling\n * ```\n * @category Voicing & Counterpoint\n */\nexport function isAugmentedMelodicInterval(prev: Note, cur: Note): boolean;\nexport function isAugmentedMelodicInterval(prev: number, cur: number): boolean;\nexport function isAugmentedMelodicInterval(prev: number | Note, cur: number | Note): boolean {\n if (typeof prev === 'number' || typeof cur === 'number') {\n return false;\n }\n const interval = spelledInterval(prev, cur);\n return interval.number >= 2 && interval.quality.startsWith('A');\n}\n\n/** Reduce an interval to its simple class in [0, 11]. */\nfunction simpleClass(semitones: number): number {\n return pitchClass(Math.abs(semitones));\n}\n\n/** Whether a simple interval class is a perfect kind (unison/octave or fifth). */\nfunction isPerfectClass(cls: number): boolean {\n return cls === 0 || cls === 7;\n}\n\n/** Whether two voices move in the same direction (similar or parallel motion). */\nfunction similarMotion(aMove: number, bMove: number): boolean {\n return aMove !== 0 && bMove !== 0 && aMove > 0 === bMove > 0;\n}\n\n/** Whether both voices actually move (neither is stationary — excludes oblique motion). */\nfunction bothVoicesMove(aMove: number, bMove: number): boolean {\n return aMove !== 0 && bMove !== 0;\n}\n\n/**\n * Whether two voices move into consecutive perfect intervals of the same kind\n * (fifth-to-fifth, octave-to-octave, unison-to-unison).\n *\n * Both true parallels (similar motion) and anti-parallels — the same perfect\n * class reached by contrary motion, e.g. octave to octave with the voices moving\n * in opposite directions — are flagged, as both are forbidden in strict two-voice\n * counterpoint. The rule requires that both voices actually move: oblique motion\n * (either voice stationary) and the no-change case (identical pitches) are excluded.\n *\n * A fifth expanding to a twelfth counts (same perfect class); a fifth moving to\n * an octave does not (different perfect kinds — the direct/hidden case owned by\n * {@link createsHiddenParallelPerfect}).\n *\n * The perfect classes are what they sound like under any spelling, so spelled\n * notes and MIDI integers give the same answer here.\n *\n * @category Voicing & Counterpoint\n */\nexport function createsParallelPerfect(aPrev: Note, aCur: Note, bPrev: Note, bCur: Note): boolean;\nexport function createsParallelPerfect(\n aPrev: number,\n aCur: number,\n bPrev: number,\n bCur: number,\n): boolean;\nexport function createsParallelPerfect(\n aPrev: number | Note,\n aCur: number | Note,\n bPrev: number | Note,\n bCur: number | Note,\n): boolean {\n const a0 = pitchOf(aPrev);\n const a1 = pitchOf(aCur);\n const b0 = pitchOf(bPrev);\n const b1 = pitchOf(bCur);\n if (!bothVoicesMove(a1 - a0, b1 - b0)) {\n return false;\n }\n const nowClass = simpleClass(a1 - b1);\n const prevClass = simpleClass(a0 - b0);\n return isPerfectClass(nowClass) && nowClass === prevClass;\n}\n\n/**\n * Whether two voices move in consecutive parallel octaves (or unisons) by\n * similar motion.\n *\n * This is a strict subset of {@link createsParallelPerfect}: a similar-motion\n * octave-to-octave is the perfect-class-zero case that predicate already flags\n * (and it additionally catches the contrary-motion anti-parallel). Callers that\n * tally parallel violations should therefore use {@link createsParallelPerfect}\n * alone to avoid double counting; this predicate remains for callers wanting a\n * dedicated similar-motion octave test.\n *\n * @category Voicing & Counterpoint\n */\nexport function createsParallelOctave(aPrev: Note, aCur: Note, bPrev: Note, bCur: Note): boolean;\nexport function createsParallelOctave(\n aPrev: number,\n aCur: number,\n bPrev: number,\n bCur: number,\n): boolean;\nexport function createsParallelOctave(\n aPrev: number | Note,\n aCur: number | Note,\n bPrev: number | Note,\n bCur: number | Note,\n): boolean {\n const a0 = pitchOf(aPrev);\n const a1 = pitchOf(aCur);\n const b0 = pitchOf(bPrev);\n const b1 = pitchOf(bCur);\n if (!similarMotion(a1 - a0, b1 - b0)) {\n return false;\n }\n return simpleClass(a1 - b1) === 0 && simpleClass(a0 - b0) === 0;\n}\n\n/**\n * Whether two voices move in consecutive parallel unisons — both landing on the\n * same pitch, having shared a pitch on the previous move.\n *\n * @category Voicing & Counterpoint\n */\nexport function createsParallelUnison(aPrev: Note, aCur: Note, bPrev: Note, bCur: Note): boolean;\nexport function createsParallelUnison(\n aPrev: number,\n aCur: number,\n bPrev: number,\n bCur: number,\n): boolean;\nexport function createsParallelUnison(\n aPrev: number | Note,\n aCur: number | Note,\n bPrev: number | Note,\n bCur: number | Note,\n): boolean {\n const a0 = pitchOf(aPrev);\n const a1 = pitchOf(aCur);\n const b0 = pitchOf(bPrev);\n const b1 = pitchOf(bCur);\n if (a1 === a0 || b1 === b0) {\n return false;\n }\n return a1 === b1 && a0 === b0;\n}\n\n/**\n * Whether two voices reach a perfect interval by similar motion from an\n * imperfect one (a hidden/direct fifth or octave).\n *\n * The traditional step exception is applied: the approach is allowed when the\n * upper of the two voices moves by step, so only leaps into the perfect\n * interval are flagged.\n *\n * @category Voicing & Counterpoint\n */\nexport function createsHiddenParallelPerfect(\n aPrev: Note,\n aCur: Note,\n bPrev: Note,\n bCur: Note,\n): boolean;\nexport function createsHiddenParallelPerfect(\n aPrev: number,\n aCur: number,\n bPrev: number,\n bCur: number,\n): boolean;\nexport function createsHiddenParallelPerfect(\n aPrev: number | Note,\n aCur: number | Note,\n bPrev: number | Note,\n bCur: number | Note,\n): boolean {\n const a0 = pitchOf(aPrev);\n const a1 = pitchOf(aCur);\n const b0 = pitchOf(bPrev);\n const b1 = pitchOf(bCur);\n const aMove = a1 - a0;\n const bMove = b1 - b0;\n if (!similarMotion(aMove, bMove)) {\n return false;\n }\n const nowClass = simpleClass(a1 - b1);\n const prevClass = simpleClass(a0 - b0);\n // Approaching the same perfect class (e.g. fifth to fifth) is a true parallel\n // owned by createsParallelPerfect; approaching a different perfect interval\n // (fifth to octave, or vice versa) is the hidden/direct case flagged here.\n if (!isPerfectClass(nowClass) || prevClass === nowClass) {\n return false;\n }\n const upperMove = a1 >= b1 ? aMove : bMove;\n if (Math.abs(upperMove) <= 2) {\n return false; // upper voice moves by step — direct interval is acceptable\n }\n return true;\n}\n\n/**\n * Whether two voices arrive at a perfect octave or unison by contrary motion\n * with the upper voice leaping down — the *ottava battuta* the sixteenth-century\n * theorists forbid.\n *\n * The stepwise arrival is allowed, as is the same octave reached with the upper\n * voice rising, so only the downward leap into the perfect class is flagged.\n *\n * @category Voicing & Counterpoint\n */\nexport function createsBattuta(aPrev: Note, aCur: Note, bPrev: Note, bCur: Note): boolean;\nexport function createsBattuta(aPrev: number, aCur: number, bPrev: number, bCur: number): boolean;\nexport function createsBattuta(\n aPrev: number | Note,\n aCur: number | Note,\n bPrev: number | Note,\n bCur: number | Note,\n): boolean {\n const a0 = pitchOf(aPrev);\n const a1 = pitchOf(aCur);\n const b0 = pitchOf(bPrev);\n const b1 = pitchOf(bCur);\n const aMove = a1 - a0;\n const bMove = b1 - b0;\n if (!bothVoicesMove(aMove, bMove) || similarMotion(aMove, bMove)) {\n return false;\n }\n if (simpleClass(a1 - b1) !== 0) {\n return false;\n }\n const upperMove = a1 >= b1 ? aMove : bMove;\n return upperMove < -2;\n}\n\n/**\n * Whether two voices overlap: the upper voice descends below where the lower\n * voice just was, or the lower voice rises above where the upper voice just was.\n * Distinct from a simultaneous voice crossing.\n *\n * @param upperPrev Previous note of the upper voice.\n * @param upperCur Current note of the upper voice.\n * @param lowerPrev Previous note of the lower voice.\n * @param lowerCur Current note of the lower voice.\n * @category Voicing & Counterpoint\n */\nexport function createsVoiceOverlap(\n upperPrev: Note,\n upperCur: Note,\n lowerPrev: Note,\n lowerCur: Note,\n): boolean;\nexport function createsVoiceOverlap(\n upperPrev: number,\n upperCur: number,\n lowerPrev: number,\n lowerCur: number,\n): boolean;\nexport function createsVoiceOverlap(\n upperPrev: number | Note,\n upperCur: number | Note,\n lowerPrev: number | Note,\n lowerCur: number | Note,\n): boolean {\n const u0 = pitchOf(upperPrev);\n const u1 = pitchOf(upperCur);\n const l0 = pitchOf(lowerPrev);\n const l1 = pitchOf(lowerCur);\n return u1 < l0 || l1 > u0;\n}\n\n/**\n * Whether two adjacent upper voices are spaced more than a maximum apart\n * (commonly an octave). Bass-to-tenor spacing is conventionally exempt, so this\n * is meant for the upper voice pairs.\n *\n * @param upper The higher voice.\n * @param lower The lower voice.\n * @param maxSemitones Maximum allowed spacing in semitones (default an octave).\n * @category Voicing & Counterpoint\n */\nexport function exceedsSpacing(upper: Note, lower: Note, maxSemitones?: number): boolean;\nexport function exceedsSpacing(upper: number, lower: number, maxSemitones?: number): boolean;\nexport function exceedsSpacing(\n upper: number | Note,\n lower: number | Note,\n maxSemitones = 12,\n): boolean {\n return Math.abs(pitchOf(upper) - pitchOf(lower)) > maxSemitones;\n}\n\n/**\n * Whether a leading tone resolves correctly upward to the tonic.\n *\n * @param prev The leading-tone note.\n * @param cur The following note.\n * @param key Key context supplying the tonic.\n * @returns True when `prev` is the leading tone and `cur` is the tonic a step above.\n * @category Voicing & Counterpoint\n */\nexport function isLeadingToneResolution(prev: Note, cur: Note, key: KeyScale): boolean;\nexport function isLeadingToneResolution(prev: number, cur: number, key: KeyScale): boolean;\nexport function isLeadingToneResolution(\n prev: number | Note,\n cur: number | Note,\n key: KeyScale,\n): boolean {\n const prevPitch = pitchOf(prev);\n const curPitch = pitchOf(cur);\n const tonic = pitchClass(key.rootPc);\n const leading = (tonic + 11) % 12;\n if (pitchClass(prevPitch) !== leading || pitchClass(curPitch) !== tonic) {\n return false;\n }\n return curPitch > prevPitch && curPitch - prevPitch <= 2;\n}\n\nexport type { VoiceIndependenceOptions, VoiceIndependenceReport } from './independence.js';\nexport { voiceIndependence } from './independence.js';\nexport { classifySpelledInterval } from './internal.js';\n","import { InvalidInputError } from '../../core/errors/index.js';\nimport { pitchClassOf as pitchClass } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport {\n assertGenerationBudget,\n assertMidiPitch,\n assertOneOf,\n} from '../../core/validation/index.js';\nimport type { Chord } from '../chord/index.js';\nimport { intervalAboveRoot, isChordMember } from '../chord/index.js';\nimport {\n createsHiddenParallelPerfect,\n createsParallelPerfect,\n createsVerticalDissonance,\n isForbiddenMelodicLeap,\n} from '../counterpoint/index.js';\nimport { isScaleTone } from '../scale/index.js';\n\n/**\n * Style policy: `strict` counterpoint vs. `pop` voice leading.\n *\n * A profile says two things. It sets the severity of each reason\n * {@link evaluateSafety} finds, which decides what is rejected outright, and it\n * carries the {@link ProfileWeights} that rank whatever survives — because most\n * of what separates the two styles is preference rather than prohibition.\n *\n * @category Arrangement & Analysis\n */\nexport type SafetyProfile = 'strict' | 'pop';\n\n/**\n * How a profile ranks the candidates its severity table has let through.\n *\n * Each field is a score contribution, added when the situation it names occurs;\n * a negative value is a penalty. The scale is arbitrary but shared, so only the\n * relative sizes matter. Nothing here can make an unsafe pitch usable: rejection\n * is the severity table's business, and these weights only order what is left.\n *\n * @category Arrangement & Analysis\n */\nexport type ProfileWeights = {\n /** A third or a sixth against the other voice. */\n imperfectConsonance: number;\n /** A unison, fifth or octave against the other voice. */\n perfectConsonance: number;\n /** A second, seventh or tritone against the other voice. */\n dissonance: number;\n /** The candidate belongs to the sounding chord. */\n chordTone: number;\n /** The two voices move in opposite directions. */\n contraryMotion: number;\n /** One voice holds while the other moves. */\n obliqueMotion: number;\n /** Both voices move the same way into a different interval. */\n similarMotion: number;\n /** Both voices move the same way, keeping the same imperfect consonance. */\n parallelImperfect: number;\n /** The candidate is a step from the previous note of its own line. */\n stepwise: number;\n /** Per semitone of melodic movement, so a leap costs more than a step. */\n melodicDistance: number;\n /** The move is a leap strict counterpoint forbids outright. */\n forbiddenLeap: number;\n /** Per semitone away from the centre of the line's register. */\n registerDrift: number;\n};\n\n/**\n * The weight table each profile ranks candidates by.\n *\n * `strict` states the sixteenth-century preferences: contrary motion is worth\n * seeking, similar motion is worth avoiding, imperfect consonances carry the\n * line, and a run of parallel thirds earns nothing. `pop` inverts two of those.\n * Parallel thirds and sixths are the harmony line an arranger writes on purpose,\n * a pedal or a held unison under a moving lead is a texture rather than a fault,\n * and similar motion is simply how two parts often move together — while a bass\n * moving against the lead is still worth preferring where it is available.\n *\n * @category Arrangement & Analysis\n */\nexport const PROFILE_WEIGHTS: Readonly<Record<SafetyProfile, Readonly<ProfileWeights>>> =\n Object.freeze({\n strict: Object.freeze({\n imperfectConsonance: 2,\n perfectConsonance: 0.5,\n dissonance: -2,\n chordTone: 1,\n contraryMotion: 2,\n obliqueMotion: 1,\n similarMotion: -1,\n parallelImperfect: 0,\n stepwise: 1,\n melodicDistance: -0.3,\n forbiddenLeap: -6,\n registerDrift: -0.02,\n }),\n pop: Object.freeze({\n imperfectConsonance: 2.5,\n perfectConsonance: 1,\n dissonance: -2,\n chordTone: 1.5,\n contraryMotion: 1.5,\n obliqueMotion: 1.25,\n similarMotion: 0,\n parallelImperfect: 2,\n stepwise: 0.75,\n melodicDistance: -0.2,\n forbiddenLeap: -6,\n registerDrift: -0.02,\n }),\n });\n\n/**\n * Every profile name, read from the weight table so an entrance check and the\n * table it guards cannot drift apart.\n */\nconst SAFETY_PROFILES = Object.keys(PROFILE_WEIGHTS) as SafetyProfile[];\n\n/**\n * Resolve a profile to the weights that rank its candidates, with per-field\n * overrides applied on top.\n *\n * @param profile The profile whose table to start from.\n * @param overrides Fields to replace; anything absent keeps the profile's value.\n * @returns The complete weight table to score with.\n * @throws If `profile` is not one of the profile names\n * ({@link InvalidInputError}). A misspelled profile scored under a default\n * table would rank candidates by a style the caller did not ask for.\n * @example\n * ```ts\n * import { profileWeights } from '@libraz/libcantus';\n * profileWeights('pop', { parallelImperfect: 4 }).contraryMotion; // 1.5 — the rest is untouched\n * ```\n * @category Arrangement & Analysis\n */\nexport function profileWeights(\n profile: SafetyProfile,\n overrides?: Partial<ProfileWeights>,\n): ProfileWeights {\n const base = PROFILE_WEIGHTS[assertOneOf(profile, SAFETY_PROFILES, 'safety profile')];\n return { ...base, ...overrides };\n}\n\n/**\n * Overall placeability verdict for a candidate pitch.\n *\n * @category Arrangement & Analysis\n */\nexport enum NoteSafety {\n Safe = 0,\n Warning = 1,\n Dissonant = 2,\n}\n\n/**\n * Bit flags describing why a pitch received its verdict.\n *\n * @category Arrangement & Analysis\n */\nexport enum ReasonFlag {\n ChordTone = 1 << 0,\n Tension = 1 << 1,\n AvoidNote = 1 << 2,\n ScaleTone = 1 << 3,\n NonScale = 1 << 4,\n OutOfRange = 1 << 5,\n Tritone = 1 << 6,\n LargeLeap = 1 << 7,\n MinorSecond = 1 << 8,\n MajorSeventh = 1 << 9,\n VerticalDissonance = 1 << 10,\n ParallelPerfect = 1 << 11,\n HiddenParallel = 1 << 12,\n VoiceCrossing = 1 << 13,\n Suspension = 1 << 14,\n NeedsResolution = 1 << 15,\n MelodicTritone = 1 << 16,\n}\n\n/**\n * A neighbouring voice's current and previous pitch.\n *\n * @category Arrangement & Analysis\n */\nexport type VoiceSnapshot = {\n pitch: number;\n prevPitch?: number;\n};\n\n/**\n * Everything needed to evaluate one candidate pitch.\n *\n * @category Arrangement & Analysis\n */\nexport type SafetyQuery = {\n profile: SafetyProfile;\n candidatePitch: number;\n prevPitch?: number;\n chord: Chord | null;\n key: KeyScale;\n otherVoices: VoiceSnapshot[];\n strongBeat: boolean;\n vocalLow?: number;\n vocalHigh?: number;\n};\n\n/**\n * The verdict, reason flags, and optional resolution guidance.\n *\n * @category Arrangement & Analysis\n */\nexport type SafetyResult = {\n safety: NoteSafety;\n reasons: number;\n resolveTo?: number;\n suggestions?: number[];\n rationale?: string;\n};\n\n/** Semitone radius searched around a rejected candidate for safe alternatives. */\nconst SUGGESTION_WINDOW = 12;\n/** Maximum number of alternative pitches returned in `SafetyResult.suggestions`. */\nconst MAX_SUGGESTIONS = 3;\n\n/** Canonical minimum verdict contributed by each non-informational reason. */\nconst REASON_SEVERITY: ReadonlyArray<\n readonly [flag: ReasonFlag, pop: NoteSafety, strict: NoteSafety]\n> = [\n [ReasonFlag.Tension, NoteSafety.Warning, NoteSafety.Warning],\n [ReasonFlag.AvoidNote, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.ScaleTone, NoteSafety.Warning, NoteSafety.Warning],\n [ReasonFlag.NonScale, NoteSafety.Dissonant, NoteSafety.Dissonant],\n [ReasonFlag.OutOfRange, NoteSafety.Warning, NoteSafety.Warning],\n [ReasonFlag.Tritone, NoteSafety.Dissonant, NoteSafety.Dissonant],\n [ReasonFlag.MelodicTritone, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.LargeLeap, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.VerticalDissonance, NoteSafety.Dissonant, NoteSafety.Dissonant],\n [ReasonFlag.ParallelPerfect, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.HiddenParallel, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.VoiceCrossing, NoteSafety.Warning, NoteSafety.Dissonant],\n [ReasonFlag.Suspension, NoteSafety.Dissonant, NoteSafety.Dissonant],\n [ReasonFlag.NeedsResolution, NoteSafety.Warning, NoteSafety.Dissonant],\n];\n\nfunction minimumSafetyForReasons(reasons: number, profile: SafetyProfile): NoteSafety {\n let minimum = NoteSafety.Safe;\n const severityIndex = profile === 'strict' ? 2 : 1;\n for (const entry of REASON_SEVERITY) {\n if (reasons & entry[0]) {\n minimum = Math.max(minimum, entry[severityIndex]) as NoteSafety;\n }\n }\n return minimum;\n}\n\n/** Whether the perfect fourth is an avoid note over this chord. */\nfunction avoidsFourth(chord: Chord): boolean {\n return chord.intervals.includes(4) && !chord.intervals.includes(5);\n}\n\n/** Whether the major seventh is an avoid note over this chord (dominant). */\nfunction avoidsMajorSeventh(chord: Chord): boolean {\n return chord.intervals.includes(4) && chord.intervals.includes(10);\n}\n\n/**\n * Whether a dissonance between two chord tones is one the chord is built from.\n *\n * A seventh chord states a seventh against its root and a dominant states the\n * tritone between its third and seventh; those intervals are the chord's\n * identity, not a clash to be prepared and resolved. A second between two chord\n * tones (a ninth voiced against the root, say) is a spacing problem rather than\n * a property of the harmony, so it stays reportable.\n */\nfunction isStructuralChordDissonance(a: number, b: number, chord: Chord): boolean {\n const rawClass = pitchClass(Math.abs(a - b));\n const aOffset = intervalAboveRoot(a, chord);\n const bOffset = intervalAboveRoot(b, chord);\n const rootSeventh =\n (aOffset === 0 && (bOffset === 10 || bOffset === 11)) ||\n (bOffset === 0 && (aOffset === 10 || aOffset === 11));\n if (rawClass !== 6 && !rootSeventh) {\n return false;\n }\n return isChordMember(a, chord) && isChordMember(b, chord);\n}\n\n/** Nearest chord tone at or below the candidate, within two semitones. */\nfunction stepResolution(pitch: number, chord: Chord): number | undefined {\n for (let delta = 1; delta <= 2; delta += 1) {\n if (isChordMember(pitch - delta, chord)) {\n return pitch - delta;\n }\n }\n return undefined;\n}\n\n/**\n * Evaluate the safety of placing a candidate pitch in a voice.\n *\n * Reasons are merged from harmonic context (chord tone / tension / avoid / scale)\n * and counterpoint (vertical dissonance, parallels, voice crossing). The `strict`\n * profile treats avoid notes and parallels as dissonant; `pop` treats them as\n * warnings. The final verdict is the worst severity among the reasons.\n *\n * @param q The candidate and its harmonic/voice-leading context.\n * @returns The verdict, reason bitmask, and optional resolution guidance.\n * @throws If the profile is not one of the profile names, or if any pitch in\n * the query is not a MIDI pitch in 0..127 ({@link InvalidInputError}).\n * @example\n * ```ts\n * import { evaluateSafety, makeChord, majorKey, NoteSafety } from '@libraz/libcantus';\n * const result = evaluateSafety({\n * profile: 'pop',\n * candidatePitch: 60, // C over a C major chord\n * chord: makeChord(0, 'maj'),\n * key: majorKey(0),\n * otherVoices: [],\n * strongBeat: true,\n * });\n * result.safety === NoteSafety.Safe; // true — C is a chord tone\n * ```\n * @category Arrangement & Analysis\n */\nexport function evaluateSafety(q: SafetyQuery, opts: EvaluateSafetyOptions = {}): SafetyResult {\n assertSafetyContext(q);\n assertMidiPitch(q.candidatePitch, 'candidatePitch');\n return evaluateInternal(q, opts.suggestions ?? true);\n}\n\n/**\n * Options for {@link evaluateSafety}.\n *\n * @category Arrangement & Analysis\n */\nexport type EvaluateSafetyOptions = {\n /**\n * Whether to search for safe replacement pitches when the verdict is not\n * safe. Turning it off skips up to two dozen extra evaluations per rejected\n * pitch, which is worth doing when only the verdict is wanted.\n *\n * @defaultValue true\n */\n suggestions?: boolean;\n};\n\n/**\n * Validate the profile and every pitch a public safety entry point reads.\n *\n * Each field named here is a MIDI pitch, and the suggestion search already\n * refuses to leave 0..127, so the same bound is the entrance contract: a pitch\n * outside it cannot be written to a note event, and a profile outside the union\n * would be scored by whichever table a lookup happened to fall back on.\n */\nfunction assertSafetyContext(q: Omit<SafetyQuery, 'candidatePitch'>): void {\n assertOneOf(q.profile, SAFETY_PROFILES, 'safety profile');\n if (q.prevPitch !== undefined) assertMidiPitch(q.prevPitch, 'prevPitch');\n if (q.vocalLow !== undefined) assertMidiPitch(q.vocalLow, 'vocalLow');\n if (q.vocalHigh !== undefined) assertMidiPitch(q.vocalHigh, 'vocalHigh');\n if (q.vocalLow !== undefined && q.vocalHigh !== undefined && q.vocalLow > q.vocalHigh) {\n throw new InvalidInputError('vocalLow must not exceed vocalHigh');\n }\n if (!Array.isArray(q.otherVoices)) {\n throw new InvalidInputError(`otherVoices must be an array; received ${typeof q.otherVoices}`);\n }\n assertGenerationBudget(q.otherVoices.length, 'other voices');\n for (let index = 0; index < q.otherVoices.length; index += 1) {\n const voice = q.otherVoices[index];\n if (!voice) continue;\n assertMidiPitch(voice.pitch, `otherVoices[${index}].pitch`);\n if (voice.prevPitch !== undefined) {\n assertMidiPitch(voice.prevPitch, `otherVoices[${index}].prevPitch`);\n }\n }\n}\n\n/**\n * Core safety evaluation.\n *\n * @param q The candidate and its harmonic/voice-leading context.\n * @param collectSuggestions Whether to search for safe alternatives on a\n * non-Safe verdict. Set false during that search itself to bound recursion.\n * @returns The verdict, reason bitmask, and optional guidance.\n */\nfunction evaluateInternal(q: SafetyQuery, collectSuggestions: boolean): SafetyResult {\n let reasons = 0;\n let level = NoteSafety.Safe;\n let resolveTo: number | undefined;\n const raise = (l: NoteSafety) => {\n if (l > level) {\n level = l;\n }\n };\n const avoidSeverity = q.profile === 'strict' ? NoteSafety.Dissonant : NoteSafety.Warning;\n const parallelSeverity = avoidSeverity;\n const pitch = q.candidatePitch;\n\n if (\n (q.vocalLow !== undefined && pitch < q.vocalLow) ||\n (q.vocalHigh !== undefined && pitch > q.vocalHigh)\n ) {\n reasons |= ReasonFlag.OutOfRange;\n raise(NoteSafety.Warning);\n }\n\n const inScale = isScaleTone(pitch, q.key);\n const chord = q.chord;\n\n // A chromatic avoid note remains chromatic. The avoid-note branch below\n // refines the resolution guidance, but must not erase the key relationship\n // or downgrade a non-scale pitch to a merely stylistic warning.\n if (!inScale && (chord === null || !isChordMember(pitch, chord))) {\n reasons |= ReasonFlag.NonScale;\n raise(NoteSafety.Dissonant);\n }\n\n if (chord && isChordMember(pitch, chord)) {\n reasons |= ReasonFlag.ChordTone;\n } else if (chord) {\n const ic = intervalAboveRoot(pitch, chord);\n const isTension = ic === 2 || ic === 5 || ic === 9;\n const avoid = (ic === 5 && avoidsFourth(chord)) || (ic === 11 && avoidsMajorSeventh(chord));\n if (avoid) {\n reasons |= ReasonFlag.AvoidNote;\n if (isTension) {\n reasons |= ReasonFlag.Tension;\n }\n reasons |= ReasonFlag.NeedsResolution;\n resolveTo = stepResolution(pitch, chord);\n raise(avoidSeverity);\n } else if (isTension && inScale) {\n reasons |= ReasonFlag.Tension;\n raise(NoteSafety.Warning);\n } else if (inScale) {\n reasons |= ReasonFlag.ScaleTone;\n raise(NoteSafety.Warning);\n }\n } else if (inScale) {\n reasons |= ReasonFlag.ScaleTone;\n raise(NoteSafety.Warning);\n }\n\n // Harmonic tritone against a chord tone. A chord tone is never flagged for a\n // tritone its own chord spells: the interval is the chord's identity, not a\n // clash with it. This covers dominant sevenths (third against seventh) and\n // equally the diminished, half-diminished, diminished-seventh and minor-sixth\n // families, where the tritone falls between two chord tones as well.\n if (chord && !isChordMember(pitch, chord)) {\n const ic = intervalAboveRoot(pitch, chord);\n const toneOffsets = chord.intervals.map((i) => ((i % 12) + 12) % 12);\n if (toneOffsets.some((t) => Math.abs(ic - t) === 6)) {\n reasons |= ReasonFlag.Tritone;\n }\n }\n\n if (q.prevPitch !== undefined) {\n // Quality flags reduce the melodic interval to its pitch-class interval so\n // compound intervals are treated uniformly with their simple form. The\n // forbidden-leap flag defers to the counterpoint rule, which preserves the\n // octave boundary (an octave leap is allowed, wider leaps are not).\n const pc = Math.abs(pitch - q.prevPitch) % 12;\n if (pc === 6) {\n reasons |= ReasonFlag.MelodicTritone;\n raise(q.profile === 'strict' ? NoteSafety.Dissonant : NoteSafety.Warning);\n }\n if (isForbiddenMelodicLeap(q.prevPitch, pitch)) {\n reasons |= ReasonFlag.LargeLeap;\n raise(parallelSeverity);\n }\n if (pc === 1) {\n reasons |= ReasonFlag.MinorSecond;\n }\n if (pc === 11) {\n reasons |= ReasonFlag.MajorSeventh;\n }\n }\n\n if (q.strongBeat) {\n const twoVoice = q.otherVoices.length === 1;\n const prev = q.prevPitch;\n for (const ov of q.otherVoices) {\n if (!createsVerticalDissonance(pitch, ov.pitch, twoVoice)) {\n continue;\n }\n if (chord && isStructuralChordDissonance(pitch, ov.pitch, chord)) {\n continue;\n }\n reasons |= ReasonFlag.VerticalDissonance;\n raise(NoteSafety.Dissonant);\n // A held pitch that was consonant on the previous step and is now\n // dissonant against the same voice is a prepared suspension.\n if (\n prev !== undefined &&\n pitch === prev &&\n ov.prevPitch !== undefined &&\n !createsVerticalDissonance(prev, ov.prevPitch, twoVoice)\n ) {\n reasons |= ReasonFlag.Suspension;\n }\n }\n }\n\n if (q.prevPitch !== undefined) {\n const prev = q.prevPitch;\n for (const ov of q.otherVoices) {\n if (ov.prevPitch === undefined) {\n continue;\n }\n // createsParallelPerfect covers every perfect class (unison/octave and\n // fifth), so no separate octave check is needed.\n if (createsParallelPerfect(prev, pitch, ov.prevPitch, ov.pitch)) {\n reasons |= ReasonFlag.ParallelPerfect;\n raise(parallelSeverity);\n }\n if (createsHiddenParallelPerfect(prev, pitch, ov.prevPitch, ov.pitch)) {\n reasons |= ReasonFlag.HiddenParallel;\n raise(parallelSeverity);\n }\n const crossedNow = pitch - ov.pitch;\n const crossedPrev = prev - ov.prevPitch;\n if (crossedNow !== 0 && crossedPrev !== 0 && crossedNow > 0 !== crossedPrev > 0) {\n reasons |= ReasonFlag.VoiceCrossing;\n raise(parallelSeverity);\n }\n }\n }\n\n // Keep reason production and verdict policy as one invariant even when a new\n // call path skips an earlier local `raise`: informational flags (ChordTone,\n // MinorSecond, MajorSeventh) may coexist with Safe; every policy flag above\n // contributes at least its table severity.\n raise(minimumSafetyForReasons(reasons, q.profile));\n\n const result: SafetyResult = { safety: level, reasons, rationale: describe(reasons, q) };\n if (resolveTo !== undefined) {\n result.resolveTo = resolveTo;\n }\n if (collectSuggestions && level !== NoteSafety.Safe) {\n const suggestions = findSafeNearby(q, pitch);\n if (suggestions.length > 0) {\n result.suggestions = suggestions;\n }\n }\n return result;\n}\n\n/**\n * Find safe alternative pitches near a rejected candidate.\n *\n * Searches outward from the candidate within `SUGGESTION_WINDOW` semitones,\n * probing the pitch below before the one above at each distance so the result\n * is deterministic and ordered by nearness.\n *\n * @param q The safety context of the rejected candidate.\n * @param candidate The candidate pitch that was not Safe.\n * @returns Up to `MAX_SUGGESTIONS` safe pitches, nearest first.\n */\nfunction findSafeNearby(q: SafetyQuery, candidate: number): number[] {\n const out: number[] = [];\n for (let d = 1; d <= SUGGESTION_WINDOW; d += 1) {\n for (const p of [candidate - d, candidate + d]) {\n if (p < 0 || p > 127) {\n continue;\n }\n if (evaluateInternal({ ...q, candidatePitch: p }, false).safety === NoteSafety.Safe) {\n out.push(p);\n if (out.length >= MAX_SUGGESTIONS) {\n return out;\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Wording for every reason flag, most to least specific.\n *\n * The list is exhaustive by construction: {@link describe} picks from it by\n * severity, so a flag with no wording here would let a rejected pitch be\n * explained by a milder reason — the seventh of a dominant reading as a plain\n * \"Chord tone\" while its verdict says dissonant.\n */\nconst REASON_TEXT: ReadonlyArray<readonly [ReasonFlag, string]> = [\n [ReasonFlag.OutOfRange, 'Outside the target vocal range'],\n [\n ReasonFlag.Suspension,\n 'Prepared suspension — a held consonance now dissonant, awaiting resolution',\n ],\n [ReasonFlag.VerticalDissonance, 'Dissonant against a sounding voice on a strong beat'],\n [ReasonFlag.Tritone, 'Tritone against the sounding harmony'],\n [ReasonFlag.MelodicTritone, 'Melodic tritone leap'],\n [ReasonFlag.AvoidNote, 'Avoid note — clashes with a chord tone a semitone away'],\n [ReasonFlag.NonScale, 'Outside the key'],\n [ReasonFlag.ParallelPerfect, 'Parallel perfect interval with another voice'],\n [ReasonFlag.HiddenParallel, 'Hidden perfect interval by similar motion'],\n [ReasonFlag.VoiceCrossing, 'Crosses another voice'],\n [ReasonFlag.LargeLeap, 'Leap wider than the profile allows'],\n [ReasonFlag.NeedsResolution, 'Dissonance awaiting a stepwise resolution'],\n [ReasonFlag.Tension, 'Chord tension'],\n [ReasonFlag.ScaleTone, 'Scale tone, not in the chord'],\n [ReasonFlag.MinorSecond, 'Melodic semitone'],\n [ReasonFlag.MajorSeventh, 'Melodic major seventh'],\n [ReasonFlag.ChordTone, 'Chord tone'],\n];\n\n/** The severity a single flag carries under a profile; Safe when purely informational. */\nfunction severityOfReason(flag: ReasonFlag, profile: SafetyProfile): NoteSafety {\n const entry = REASON_SEVERITY.find(([candidate]) => candidate === flag);\n return entry === undefined ? NoteSafety.Safe : entry[profile === 'strict' ? 2 : 1];\n}\n\n/**\n * Build a short human explanation from the reason flags.\n *\n * The flag driving the verdict wins: the one whose severity under this profile\n * is highest, ties going to the more specific wording. Explaining a rejected\n * pitch by an informational flag it also carries would tell the caller nothing\n * about what to fix.\n */\nfunction describe(reasons: number, q: SafetyQuery): string {\n let best: string | undefined;\n let bestSeverity = -1;\n for (const [flag, text] of REASON_TEXT) {\n if (!(reasons & flag)) {\n continue;\n }\n const severity = severityOfReason(flag, q.profile);\n if (severity > bestSeverity) {\n bestSeverity = severity;\n best = text;\n }\n }\n if (best !== undefined) {\n return best;\n }\n return q.chord ? 'Placeable' : 'No chord context';\n}\n\n/**\n * Enumerate placeable pitches in a range, chord tones first, descending.\n *\n * @param q The safety context minus the candidate pitch.\n * @param pitchLow Lowest MIDI pitch to consider (inclusive).\n * @param pitchHigh Highest MIDI pitch to consider (inclusive).\n * @returns Placeable pitches (non-dissonant), chord tones before others, each group descending.\n * @throws If either bound is not a MIDI pitch in 0..127, reversed, or exceeds\n * the generation budget, or if the context names an unknown profile.\n * @example\n * ```ts\n * import { enumerateSafePitches, makeChord, majorKey } from '@libraz/libcantus';\n * const pitches = enumerateSafePitches(\n * { profile: 'pop', chord: makeChord(0, 'maj'), key: majorKey(0), otherVoices: [], strongBeat: true },\n * 60,\n * 72,\n * );\n * pitches; // placeable pitches in [60, 72], chord tones first, each group descending\n * ```\n * @category Arrangement & Analysis\n */\nexport function enumerateSafePitches(\n q: Omit<SafetyQuery, 'candidatePitch'>,\n pitchLow: number,\n pitchHigh: number,\n): number[] {\n assertSafetyContext(q);\n const chordTones: number[] = [];\n const others: number[] = [];\n // The bounds are MIDI pitches, and so is everything enumerated between them:\n // a pitch outside 0..127 cannot be written to a note event, so it is refused\n // here rather than handed to an arranger that has nowhere to put it.\n assertMidiPitch(pitchLow, 'pitchLow');\n assertMidiPitch(pitchHigh, 'pitchHigh');\n if (pitchLow > pitchHigh) {\n throw new InvalidInputError(\n `pitchLow must not exceed pitchHigh; received ${pitchLow} > ${pitchHigh}`,\n );\n }\n assertGenerationBudget(pitchHigh - pitchLow + 1, 'safe pitch candidates');\n const low = Math.max(pitchLow, q.vocalLow ?? pitchLow);\n const high = Math.min(pitchHigh, q.vocalHigh ?? pitchHigh);\n for (let pitch = high; pitch >= low; pitch -= 1) {\n const result = evaluateInternal({ ...q, candidatePitch: pitch }, false);\n if (result.safety === NoteSafety.Dissonant) {\n continue;\n }\n if (result.reasons & ReasonFlag.ChordTone) {\n chordTones.push(pitch);\n } else {\n others.push(pitch);\n }\n }\n return [...chordTones, ...others];\n}\n","/**\n * Lead-sheet chord symbol parsing and formatting.\n *\n * Bridges the informal `<root><quality>[/<bass>]` notation musicians write\n * (`Cmaj7`, `F#m7b5`, `Bb7`, `C/G`) and the library's structured {@link Chord}\n * type, so callers can accept/emit plain text at the edges of the API.\n *\n * The root and the slash bass are note names, so they are read and written in\n * whichever notation system the caller names — a German chart's `H7`, `As7` and\n * `Ges/B` included. Unlike a note name, a chord symbol is never attributed to a\n * system on its own: German `B` is a B flat and English `B` a B natural, and a\n * symbol carries no `dur`/`moll` word to break the tie, so the most common\n * symbol in the vocabulary would otherwise change chord with the source it came\n * from. With no system given a symbol is English.\n */\n\nimport {\n InvalidInputError,\n type ParseResult,\n parseFailure,\n unwrapParse,\n} from '../../core/errors/index.js';\nimport {\n formatNote,\n midiToNote,\n type Note,\n type NoteNameOptions,\n type NoteNameSystem,\n noteToPitchClass,\n parseNote,\n pitchClassOf as pitchClass,\n transposeNote,\n tryParseNote,\n} from '../../core/pitch/index.js';\nimport { assertFiniteNumber, assertFiniteSemitones } from '../../core/validation/index.js';\nimport type {\n Alteration,\n AlteredDegree,\n Chord,\n ChordBase,\n ChordQuality,\n ChordSeventh,\n ChordSpec,\n PitchSpelling,\n} from '../chord/index.js';\nimport { chordFromSpec, chordQualities, chordSpecOf } from '../chord/index.js';\nimport { chordSpecForQuality, exactChordSpecQuality } from '../chord/spec.js';\n\n/**\n * Matches an English chord root: a letter A-G (case-insensitive, like\n * {@link parseNote}) with an optional single-direction accidental.\n */\nconst ROOT_RE = /^[A-Ga-g](?:#{1,2}|b{1,2}|x)?/;\n\n/** Matches a bare English note usable as a slash-chord bass (no octave). */\nconst BASS_RE = /^[A-Ga-g](?:#{1,2}|b{1,2}|x)?$/;\n\n/**\n * The system a symbol is read and written in when the caller names none.\n *\n * A German reading is something a caller asks for, never something inferred:\n * see this module's own documentation for why chord symbols do not detect.\n */\nconst DEFAULT_SYSTEM: NoteNameSystem = 'english';\n\n/** A note every system can write, used only to validate a requested system. */\nconst SYSTEM_PROBE: Note = { letter: 0, alter: 0 };\n\n/** Recognized quality suffixes, keyed by their exact lead-sheet spelling. */\nconst QUALITY_MAP: Record<string, ChordQuality> = {\n '': 'maj',\n maj: 'maj',\n m: 'min',\n min: 'min',\n '-': 'min',\n dim: 'dim',\n '°': 'dim',\n aug: 'aug',\n '+': 'aug',\n maj7: 'maj7',\n M7: 'maj7',\n Δ: 'maj7',\n Δ7: 'maj7',\n m7: 'min7',\n min7: 'min7',\n '-7': 'min7',\n '7': 'dom7',\n dim7: 'dim7',\n '°7': 'dim7',\n m7b5: 'm7b5',\n ø: 'm7b5',\n ø7: 'm7b5',\n min7b5: 'm7b5',\n mMaj7: 'minMaj7',\n mM7: 'minMaj7',\n minMaj7: 'minMaj7',\n mMaj9: 'minMaj9',\n mM9: 'minMaj9',\n minMaj9: 'minMaj9',\n mMaj11: 'minMaj11',\n mM11: 'minMaj11',\n minMaj11: 'minMaj11',\n mMaj13: 'minMaj13',\n mM13: 'minMaj13',\n minMaj13: 'minMaj13',\n aug7: 'aug7',\n '+7': 'aug7',\n '7#5': 'aug7',\n augMaj7: 'augMaj7',\n augM7: 'augMaj7',\n '+maj7': 'augMaj7',\n '+M7': 'augMaj7',\n majb5: 'majb5',\n 'maj(b5)': 'majb5',\n '(b5)': 'majb5',\n '6': '6',\n m6: 'min6',\n min6: 'min6',\n '6/9': '6/9',\n '69': '6/9',\n sus2: 'sus2',\n sus: 'sus4',\n sus4: 'sus4',\n add9: 'add9',\n add11: 'add11',\n maj9: 'maj9',\n M9: 'maj9',\n m9: 'min9',\n min9: 'min9',\n '9': 'dom9',\n '7b9': '7b9',\n '7#9': '7#9',\n '7#11': '7#11',\n '7b13': '7b13',\n '11': '11',\n '13': '13',\n '5': '5',\n '7sus4': '7sus4',\n '7sus': '7sus4',\n '9sus4': '11',\n '9sus': '11',\n '7b5': '7b5',\n '7alt': '7alt',\n alt: '7alt',\n '13b9': '13b9',\n maj13: 'maj13',\n M13: 'maj13',\n 'maj7#11': 'maj7#11',\n 'M7#11': 'maj7#11',\n m11: 'min11',\n min11: 'min11',\n m13: 'min13',\n min13: 'min13',\n madd9: 'minAdd9',\n 'min(add9)': 'minAdd9',\n 'm(add9)': 'minAdd9',\n '6add9': '6/9',\n 'm6/9': 'min6/9',\n m69: 'min6/9',\n 'min6/9': 'min6/9',\n // ASCII stand-ins for the degree and half-diminished glyphs, which most\n // lead sheets and chord-chart text files use instead of the typographic ones.\n o: 'dim',\n o7: 'dim7',\n h: 'm7b5',\n h7: 'm7b5',\n};\n\n/** Canonical lead-sheet suffix emitted for each chord quality when formatting. */\nconst CANONICAL_SUFFIX: Record<ChordQuality, string> = {\n maj: '',\n min: 'm',\n dim: 'dim',\n aug: 'aug',\n maj7: 'maj7',\n min7: 'm7',\n dom7: '7',\n dim7: 'dim7',\n m7b5: 'm7b5',\n minMaj7: 'mMaj7',\n minMaj9: 'mMaj9',\n minMaj11: 'mMaj11',\n minMaj13: 'mMaj13',\n aug7: 'aug7',\n augMaj7: 'augMaj7',\n majb5: 'maj(b5)',\n '6': '6',\n min6: 'm6',\n '6/9': '6/9',\n sus2: 'sus2',\n sus4: 'sus4',\n add9: 'add9',\n add11: 'add11',\n maj9: 'maj9',\n min9: 'm9',\n dom9: '9',\n '7b9': '7b9',\n '7#9': '7#9',\n '7#11': '7#11',\n '7b13': '7b13',\n '11': '11',\n '13': '13',\n '5': '5',\n '7sus4': '7sus4',\n '7b5': '7b5',\n '7alt': '7alt',\n '13b9': '13b9',\n maj13: 'maj13',\n 'maj7#11': 'maj7#11',\n min11: 'm11',\n min13: 'm13',\n minAdd9: 'madd9',\n 'min6/9': 'm6/9',\n};\n\n/**\n * The quality a suffix names outright, or undefined when it names none.\n *\n * The own-property check is what stops `'constructor'` and `'toString'` from\n * resolving through `Object.prototype` and reaching the chord builder with a\n * quality it has no interval template for.\n */\nfunction knownQuality(suffix: string): ChordQuality | undefined {\n return Object.hasOwn(QUALITY_MAP, suffix) ? QUALITY_MAP[suffix] : undefined;\n}\n\n/** How a base is written where a symbol opens with one. */\nconst BASE_TOKENS: readonly (readonly [string, ChordBase | 'major'])[] = [\n ['sus2', 'sus2'],\n ['sus4', 'sus4'],\n ['sus', 'sus4'],\n ['min', 'min'],\n ['dim', 'dim'],\n ['aug', 'aug'],\n ['maj', 'major'],\n ['m', 'min'],\n ['-', 'min'],\n ['o', 'dim'],\n ['°', 'dim'],\n ['+', 'aug'],\n ['M', 'major'],\n ['Δ', 'major'],\n ['^', 'major'],\n];\n\n/**\n * The markers that say a seventh is major.\n *\n * The glyphs say it on their own — `CΔ` is a major seventh — while the words do\n * not: `Cmaj` is the plain triad, and `CmMaj` only reads as a major seventh\n * because the base already took the letter before it.\n */\nconst MAJOR_MARKERS: readonly (readonly [string, 'word' | 'glyph'])[] = [\n ['maj', 'word'],\n ['Maj', 'word'],\n ['M', 'word'],\n ['Δ', 'glyph'],\n ['^', 'glyph'],\n];\n\n/** The extension numbers a symbol stacks a chord up to. */\nconst EXTENSION_NUMBERS = [13, 11, 9, 7, 6] as const;\n\n/** The degree each `add` figure names, so `add2` and `add9` are one tone. */\nconst ADDED_DEGREES: Record<string, number> = {\n '2': 9,\n '4': 11,\n '6': 6,\n '9': 9,\n '11': 11,\n '13': 13,\n};\n\n/** The degrees a symbol can write an accidental against. */\nconst ALTERABLE: Record<string, AlteredDegree> = { '5': 5, '9': 9, '11': 11, '13': 13 };\n\n/** The degrees a symbol can ask a chord to leave out. */\nconst OMITTABLE: Record<string, number> = { '1': 1, '3': 3, '5': 5 };\n\n/** A chord read token by token, before it settles into a {@link ChordSpec}. */\ntype SpecDraft = {\n base: ChordBase;\n seventh?: ChordSeventh;\n alterations: Map<AlteredDegree, -1 | 0 | 1>;\n additions: Set<number>;\n omissions: Set<number>;\n /** Whether a `maj`/`M`/`Δ` marker has already claimed the seventh. */\n major: boolean;\n};\n\n/** The longest token in `table` that `text` carries at `at`. */\nfunction matchToken<T>(\n text: string,\n at: number,\n table: readonly (readonly [string, T])[],\n): { value: T; next: number } | undefined {\n let found: { value: T; next: number } | undefined;\n for (const [token, value] of table) {\n if (text.startsWith(token, at) && (found === undefined || at + token.length > found.next)) {\n found = { value, next: at + token.length };\n }\n }\n return found;\n}\n\n/** The number token at `at`, longest first so 11 and 13 are not read as 1. */\nfunction matchNumber(text: string, at: number): { value: number; next: number } | undefined {\n for (const value of EXTENSION_NUMBERS) {\n const token = String(value);\n if (text.startsWith(token, at)) {\n return { value, next: at + token.length };\n }\n }\n return undefined;\n}\n\n/**\n * Stack the draft up to an extension number.\n *\n * A number names the top of a stack of thirds, not a single tone: `13` is a\n * seventh with a ninth and a thirteenth over it. The eleventh is left out of a\n * thirteenth chord, which is how a chart voices one.\n */\nfunction stackTo(draft: SpecDraft, top: number): void {\n if (top === 6) {\n draft.additions.add(6);\n return;\n }\n draft.seventh = draft.major ? 'maj7' : draft.base === 'dim' ? 'dim7' : 'min7';\n if (top >= 9) {\n draft.alterations.set(9, 0);\n }\n if (top === 11) {\n draft.alterations.set(11, 0);\n }\n if (top === 13) {\n draft.alterations.set(13, 0);\n }\n}\n\n/**\n * Apply one figure written after the chord's core, or report that it is none.\n *\n * The same grammar reads a figure written bare (`7b9`) and one written inside\n * brackets (`7(b9,#11)`), so the two spellings of a chart's tensions cannot\n * drift apart.\n */\nfunction applyFigure(draft: SpecDraft, text: string, at: number): number | undefined {\n const base = matchToken(text, at, [\n ['sus2', 'sus2'],\n ['sus4', 'sus4'],\n ['sus', 'sus4'],\n ] as const);\n if (base !== undefined) {\n draft.base = base.value;\n return base.next;\n }\n if (text.startsWith('alt', at)) {\n // The altered dominant, whichever way it is reached: a raised fifth and a\n // raised ninth over a dominant seventh.\n draft.base = 'aug';\n draft.seventh = 'min7';\n draft.alterations.set(9, 1);\n return at + 3;\n }\n if (text.startsWith('add', at)) {\n const degree = matchNumberToken(text, at + 3, ADDED_DEGREES);\n if (degree === undefined) {\n return undefined;\n }\n draft.additions.add(degree.value);\n return degree.next;\n }\n const omitted = matchToken(text, at, [\n ['omit', 4],\n ['no', 2],\n ] as const);\n if (omitted !== undefined) {\n const degree = matchNumberToken(text, omitted.next, OMITTABLE);\n if (degree === undefined) {\n return undefined;\n }\n draft.omissions.add(degree.value);\n return degree.next;\n }\n const accidental = matchToken(text, at, [\n ['b', -1],\n ['#', 1],\n ] as const);\n const from = accidental?.next ?? at;\n const degree = matchNumberToken(text, from, ALTERABLE);\n if (degree === undefined) {\n return undefined;\n }\n draft.alterations.set(degree.value, accidental?.value ?? 0);\n return degree.next;\n}\n\n/** The longest degree number in `table` that `text` carries at `at`. */\nfunction matchNumberToken<T>(\n text: string,\n at: number,\n table: Record<string, T>,\n): { value: T; next: number } | undefined {\n const entries = Object.entries(table).map(([token, value]) => [token, value] as const);\n return matchToken(text, at, entries);\n}\n\n/**\n * Read a quality suffix as a chord, or undefined when it is not one.\n *\n * The suffix is read structurally — a base, a marker, an extension number, then\n * any number of figures written bare or in brackets — so a combination of\n * tensions no quality name covers is read as readily as one that has a name.\n * Returning undefined rather than throwing is what lets the caller keep trying\n * shorter roots when a system writes its accidental as an affix.\n */\nfunction structuralSpec(text: string): ChordSpec | undefined {\n let core = '';\n const groups: string[] = [];\n let inside = false;\n let buffer = '';\n for (const char of text) {\n if (char === '(') {\n if (inside) {\n return undefined;\n }\n inside = true;\n buffer = '';\n } else if (char === ')') {\n if (!inside) {\n return undefined;\n }\n inside = false;\n groups.push(buffer);\n } else if (inside) {\n buffer += char;\n } else {\n core += char;\n }\n }\n if (inside) {\n return undefined;\n }\n const draft: SpecDraft = {\n base: 'maj',\n alterations: new Map(),\n additions: new Set(),\n omissions: new Set(),\n major: false,\n };\n let at = 0;\n const opening = matchToken(core, at, BASE_TOKENS);\n let markerGlyph = false;\n let markerAfterBase = false;\n if (opening !== undefined) {\n at = opening.next;\n if (opening.value === 'major') {\n draft.major = true;\n markerGlyph = core.startsWith('Δ') || core.startsWith('^');\n } else {\n draft.base = opening.value;\n }\n }\n if (!draft.major) {\n const marker = matchToken(core, at, MAJOR_MARKERS);\n if (marker !== undefined) {\n draft.major = true;\n markerGlyph = marker.value === 'glyph';\n markerAfterBase = opening !== undefined;\n at = marker.next;\n }\n }\n const number = matchNumber(core, at);\n if (number !== undefined) {\n at = number.next;\n stackTo(draft, number.value);\n } else if (draft.major && (markerGlyph || markerAfterBase)) {\n // A marker with no number after it names the seventh itself, but only where\n // it is not the whole suffix: `Cmaj` is the triad and `CΔ` the seventh.\n draft.seventh = 'maj7';\n }\n while (at < core.length) {\n // Between two figures a '/' separates them, as it already does inside\n // brackets: the added sixth and ninth of a `6/9` are written that way, and\n // so are the tensions a chart writes after one. A leading or trailing slash\n // separates nothing, so it is left to fail as the stray character it is.\n if (core[at] === '/' && at > 0 && at + 1 < core.length) {\n at += 1;\n continue;\n }\n const next = applyFigure(draft, core, at);\n if (next === undefined || next === at) {\n return undefined;\n }\n at = next;\n }\n for (const group of groups) {\n for (const figure of group.split(/[,/\\s]+/)) {\n if (figure.length === 0) {\n continue;\n }\n const next = applyFigure(draft, figure, 0);\n if (next !== figure.length) {\n return undefined;\n }\n }\n }\n return {\n rootPc: 0,\n base: draft.base,\n ...(draft.seventh === undefined ? {} : { seventh: draft.seventh }),\n alterations: [...draft.alterations].map(([degree, alter]) => ({ degree, alter })),\n additions: [...draft.additions],\n omissions: [...draft.omissions],\n };\n}\n\n/**\n * The chord a quality suffix names, or undefined when it names none.\n *\n * A name in the alias table wins, so every spelling a chart already uses keeps\n * the exact reading it had; anything else is read structurally.\n */\nfunction suffixSpec(suffix: string): ChordSpec | undefined {\n const quality = knownQuality(suffix);\n return quality === undefined ? structuralSpec(suffix) : chordSpecForQuality(quality, 0);\n}\n\n/**\n * Reject a notation system the naming layer has no table for.\n *\n * A root is found by trying to read one, and a name a system cannot read is\n * simply not a root, so without this an unknown `system` would be reported as\n * an invalid chord symbol rather than as the invalid option it is.\n */\nfunction assertNamingSystem(system: NoteNameSystem): void {\n formatNote(SYSTEM_PROBE, { system });\n}\n\n/**\n * Read `text` as an octave-less note in `system`, or undefined when it is none.\n *\n * A chord root carries no register: `H7` is a dominant seventh, not the German\n * H in octave 7, so a name that swallowed the quality's digits is refused here\n * instead of being taken for a root.\n */\nfunction bareNote(text: string, system: NoteNameSystem): Note | undefined {\n const read = tryParseNote(text, { system });\n if (!read.ok) {\n return undefined;\n }\n return read.value.octave === undefined ? read.value : undefined;\n}\n\n/** A chord root read off the front of a symbol, and the text left after it. */\ntype RootSplit = { root: Note; rest: string };\n\n/**\n * Every way the front of `text` reads as a chord root, longest root first.\n *\n * An English root is a letter and an optional accidental, which the pattern\n * settles in one step. The other systems write the accidental as an affix that\n * can also open a quality suffix — German `As` is the A flat, but `Asus4` is an\n * A chord — so every prefix is offered and the caller keeps the longest one\n * whose remainder it recognizes as a quality.\n */\nfunction rootSplits(text: string, system: NoteNameSystem): RootSplit[] {\n if (system === 'english') {\n const match = text.match(ROOT_RE);\n const token = match?.[0];\n return token === undefined\n ? []\n : [{ root: parseNote(token, { system }), rest: text.slice(token.length) }];\n }\n assertNamingSystem(system);\n const splits: RootSplit[] = [];\n for (let end = text.length; end > 0; end -= 1) {\n const root = bareNote(text.slice(0, end), system);\n if (root !== undefined) {\n splits.push({ root, rest: text.slice(end) });\n }\n }\n return splits;\n}\n\n/** Read a slash-chord bass, which is a bare note in the symbol's own system. */\nfunction bassNote(text: string, system: NoteNameSystem): Note | undefined {\n if (system === 'english') {\n return BASS_RE.test(text) ? parseNote(text, { system }) : undefined;\n }\n return bareNote(text, system);\n}\n\n/** Build a chord and record the parsed spellings as enharmonic hints. */\nfunction makeSpelledChord(root: Note, spec: ChordSpec, bass?: Note): Chord {\n const rooted: ChordSpec = { ...spec, rootPc: noteToPitchClass(root) };\n if (bass !== undefined) {\n rooted.bassPc = noteToPitchClass(bass);\n }\n const chord = chordFromSpec(rooted);\n chord.rootSpelling = { letter: root.letter, alter: root.alter };\n if (bass !== undefined) {\n chord.bassSpelling = { letter: bass.letter, alter: bass.alter };\n }\n return chord;\n}\n\n/**\n * The chord a root and the text after it name, or undefined when that text\n * names no chord this module can read.\n */\nfunction chordAfterRoot(root: Note, rest: string, system: NoteNameSystem): Chord | undefined {\n // Split at the last '/' so the '6/9' quality's own slash never masks a\n // trailing slash bass (e.g. 'C6/9/E').\n const slashIndex = rest.lastIndexOf('/');\n if (slashIndex !== -1) {\n const slashSpec = suffixSpec(rest.slice(0, slashIndex));\n const bass = slashSpec === undefined ? undefined : bassNote(rest.slice(slashIndex + 1), system);\n if (slashSpec !== undefined && bass !== undefined) {\n return makeSpelledChord(root, slashSpec, bass);\n }\n }\n const spec = suffixSpec(rest);\n return spec === undefined ? undefined : makeSpelledChord(root, spec);\n}\n\n/**\n * Parse a lead-sheet chord symbol into a {@link Chord}.\n *\n * Accepts `<root><quality>[/<bass>]`, e.g. `Cmaj7`, `F#m7b5`, `Bb7`, `C/G`,\n * `C6/9`, `C6/9/E`. Root and bass letters are case-insensitive, matching\n * {@link parseNote}. A `/` is ambiguous between a slash bass and the `6/9`\n * quality; the text splits at the last `/` whose right-hand side is a valid\n * bare note preceded by a recognized quality, so `6/9` (with or without a\n * further slash bass) parses as a quality.\n *\n * The root and bass are read in `system`, so a German chart's `H7`, `As7` and\n * `Ges/B` are read by asking for `'german'`. The system is never inferred: with\n * none given the symbol is English, where `B` is the B natural and `H` no note\n * at all. Case carries nothing in a chord symbol — unlike a German key name,\n * where the case of the tonic is the mode — so the quality suffix alone says\n * major or minor. Where a German root's flat suffix could equally open the\n * quality, the longest root that leaves a known quality behind wins: `Asus4` is\n * an A suspended chord and `Assus4` the A flat one.\n *\n * The quality suffix is read structurally when no name covers it, so a chart's\n * bracketed tensions parse as readily as the names do: `Cmaj7(#11)`,\n * `C7(b9,#11)`, `C7(13)`, `Csus4(add9)`, `C-Δ9`, `C6/9(#11)`. A `/` between two\n * figures separates them as it does inside brackets, which is what the sixth\n * and ninth of a `6/9` are. Such a chord carries the tones it names and reports\n * the nearest quality name (see {@link chordSpecQuality}); read\n * {@link chordSpecOf} for its structure.\n *\n * The parsed chord carries the root/bass spellings as enharmonic hints\n * (`rootSpelling`/`bassSpelling`) so {@link formatChordSymbol} can reproduce\n * flat spellings such as `Bbmaj7` instead of respelling them with sharps.\n *\n * @param text The chord symbol text.\n * @param opts `system` reads the root and bass in that notation system instead\n * of English.\n * @returns The parsed chord.\n * @throws If the root or quality is not recognized in that system. Use\n * {@link tryParseChordSymbol} where failure is ordinary, such as a chord\n * field read on every keystroke.\n * @example\n * ```ts\n * import { parseChordSymbol, formatChordSymbol } from '@libraz/libcantus';\n * const c = parseChordSymbol('Bbmaj7');\n * formatChordSymbol(c); // 'Bbmaj7' — flat spelling is preserved on round-trip\n * parseChordSymbol('H7', { system: 'german' }).rootPc; // 11 — the B natural\n * parseChordSymbol('B', { system: 'german' }).rootPc; // 10 — the German B flat\n * ```\n * @category Chords\n */\nexport function parseChordSymbol(text: string, opts?: NoteNameOptions): Chord {\n return unwrapParse(tryParseChordSymbol(text, opts));\n}\n\n/**\n * Parse a lead-sheet chord symbol, reporting failure instead of throwing it.\n *\n * The same reading as {@link parseChordSymbol} — that function is this one with\n * its error thrown — for the callers where a symbol that does not parse yet is\n * the normal state of the input rather than a fault: a chord field says what is\n * wrong with what has been typed so far, and does not need a `try` around every\n * keystroke to do it.\n *\n * @param text The chord symbol text.\n * @param opts `system` reads the root and bass in that notation system instead\n * of English.\n * @returns The chord, or the error explaining why the text is not one.\n * @example\n * ```ts\n * import { tryParseChordSymbol } from '@libraz/libcantus';\n * const result = tryParseChordSymbol('Cmaj7(#11)');\n * result.ok ? result.value.intervals : result.error.message;\n * ```\n * @category Chords\n */\nexport function tryParseChordSymbol(text: string, opts?: NoteNameOptions): ParseResult<Chord> {\n try {\n if (typeof text !== 'string') {\n throw new InvalidInputError(`chord symbol must be a string; received ${typeof text}`);\n }\n const system = opts?.system ?? DEFAULT_SYSTEM;\n const trimmed = text\n .trim()\n .replaceAll('♯', '#')\n .replaceAll('♭', 'b')\n .replaceAll('𝄪', 'x')\n .replaceAll('𝄫', 'bb')\n // Both delta glyphs in circulation stand for the same major seventh.\n .replaceAll('∆', 'Δ');\n const splits = rootSplits(trimmed, system);\n if (splits.length === 0) {\n throw new InvalidInputError(`Invalid chord symbol: ${text}`);\n }\n for (const { root, rest } of splits) {\n const chord = chordAfterRoot(root, rest, system);\n if (chord !== undefined) {\n return { ok: true, value: chord };\n }\n }\n throw new InvalidInputError(`Unrecognized chord quality: ${text}`);\n } catch (error) {\n return parseFailure(error);\n }\n}\n\n/**\n * Anything that names a chord: a chord symbol, plain chord data, or a value\n * that serializes to chord data such as the `Chord` class.\n *\n * @category Chords\n */\nexport type ChordLike =\n | string\n | Chord\n | {\n /** The chord data this value stands for. */\n toJSON(): Chord;\n };\n\n/** Validate plain chord data and return it in the canonical shape. */\nfunction normalizedChord(data: Chord): Chord {\n if (!Array.isArray(data.intervals)) {\n throw new InvalidInputError('chord.intervals must be an array of semitone offsets');\n }\n const copy: Chord = {\n rootPc: pitchClass(assertFiniteNumber(data.rootPc, 'chord.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 = pitchClass(assertFiniteNumber(data.bassPc, 'chord.bassPc'));\n }\n // Spelling hints are carried through untouched. They are the caller's record\n // of how the chord is written, and deriving or discarding one here would\n // overrule a decision made where the key was known.\n if (data.rootSpelling !== undefined) {\n copy.rootSpelling = data.rootSpelling;\n }\n if (data.bassSpelling !== undefined) {\n copy.bassSpelling = data.bassSpelling;\n }\n if (data.toneSpellings !== undefined) {\n copy.toneSpellings = data.toneSpellings;\n }\n return copy;\n}\n\n/**\n * Resolve any chord-shaped value to plain {@link Chord} data.\n *\n * The counterpart of {@link toSpelledInterval} for chords: an entry point takes\n * whatever form the caller has — the symbol a chart holds, the data the chord\n * module returns, or a `Chord` instance — and gets one shape back. An instance\n * is accepted through its `toJSON` method rather than by its type, so the\n * layers below the model can read a class without importing it.\n *\n * @param value A chord symbol, plain chord data, or a value whose `toJSON`\n * returns chord data.\n * @returns The validated chord data, with root and bass reduced to pitch\n * classes and any spelling hint carried through.\n * @throws If the value names no chord, or an interval, root, or bass is not a\n * finite number.\n * @example\n * ```ts\n * import { toChordData } from '@libraz/libcantus';\n * toChordData('Cmaj7').intervals; // [0, 4, 7, 11]\n * toChordData({ rootPc: 0, quality: 'maj', intervals: [0, 4, 7] }).rootPc; // 0\n * ```\n * @category Chords\n */\nexport function toChordData(value: ChordLike): Chord {\n if (typeof value === 'string') {\n return parseChordSymbol(value);\n }\n if (typeof value === 'object' && value !== null) {\n const data = 'toJSON' in value && typeof value.toJSON === 'function' ? value.toJSON() : value;\n return normalizedChord(data as Chord);\n }\n throw new InvalidInputError(\n `chord must be a chord symbol or chord data; received ${typeof value}`,\n );\n}\n\n/**\n * A root or bass written the way a chord symbol writes it.\n *\n * German note names are lowercase, but a German chord symbol capitalizes its\n * root — `Es`, `Fis`, `H7` — and leaves the quality suffix to say major or\n * minor. Since case means nothing here, reading ignores it and writing settles\n * on the capitalized form charts use. Every other system writes its name as the\n * naming layer does.\n */\nfunction symbolName(note: Note, system: NoteNameSystem): string {\n const name = formatNote(note, { system });\n return system === 'german' ? `${name.slice(0, 1).toUpperCase()}${name.slice(1)}` : name;\n}\n\n/**\n * Name a pitch class, preferring a spelling hint when it is still valid.\n *\n * A hint is used only when no explicit sharp/flat preference was given and the\n * hint still resolves to `pc` (a stale hint left over after transposition is\n * ignored). Otherwise the pitch class is respelled from the requested table,\n * falling back to `inheritFlats` so an unhinted slash bass follows the side its\n * root was spelled on rather than flipping to sharps inside one symbol.\n */\nfunction pitchClassName(\n pc: number,\n hint: PitchSpelling | undefined,\n system: NoteNameSystem,\n flats?: boolean,\n inheritFlats?: boolean,\n): string {\n if (flats === undefined && hint !== undefined && noteToPitchClass(hint) === pc) {\n return symbolName(hint, system);\n }\n const note = midiToNote(60 + pc, (flats ?? inheritFlats) ? 'flat' : 'sharp');\n return symbolName({ letter: note.letter, alter: note.alter }, system);\n}\n\n/**\n * The named chords a symbol can write as its core, with the spec each stands\n * for.\n *\n * A core carries no tension of its own beyond the ones a chart writes into the\n * name itself — the unaltered ninth of an `m9`, the flattened fifth of a `7b5`\n * — so what is left over is written as figures rather than swallowed by a name\n * that does not say it.\n */\nconst CORE_CANDIDATES: readonly (readonly [ChordQuality, ChordSpec])[] = chordQualities()\n .map((quality) => [quality, chordSpecForQuality(quality, 0)] as const)\n .filter(([, spec]) => spec.alterations.every(({ degree, alter }) => degree === 5 || alter === 0));\n\n/** Whether `part` is one of the alterations `spec` carries. */\nfunction carriesAlteration(spec: ChordSpec, part: Alteration): boolean {\n return spec.alterations.some(\n ({ degree, alter }) => degree === part.degree && alter === part.alter,\n );\n}\n\n/**\n * The largest named chord a spec can be written as before its figures.\n *\n * Every part of the name must be a part of the chord: same base and seventh,\n * and no alteration, addition or omission the chord does not have. The largest\n * such name is the core, which is what makes a minor ninth with a raised\n * eleventh `Cm9(#11)` rather than `Cm7(9,#11)`.\n */\nfunction coreQuality(spec: ChordSpec): ChordQuality | undefined {\n let core: ChordQuality | undefined;\n let named = -1;\n for (const [quality, candidate] of CORE_CANDIDATES) {\n const size = candidate.alterations.length + candidate.additions.length;\n if (\n size <= named ||\n candidate.base !== spec.base ||\n candidate.seventh !== spec.seventh ||\n !candidate.alterations.every((part) => carriesAlteration(spec, part)) ||\n !candidate.additions.every((degree) => spec.additions.includes(degree)) ||\n !candidate.omissions.every((degree) => spec.omissions.includes(degree))\n ) {\n continue;\n }\n core = quality;\n named = size;\n }\n return core;\n}\n\n/** How each base opens a suffix that no quality name covers. */\nconst BASE_SUFFIX: Record<ChordBase, string> = {\n maj: '',\n min: 'm',\n dim: 'dim',\n aug: 'aug',\n sus2: 'sus2',\n sus4: 'sus4',\n power: '5',\n};\n\n/** How each seventh is written where it opens a suffix of its own. */\nconst SEVENTH_SUFFIX: Record<ChordSeventh, string> = { maj7: 'maj7', min7: '7', dim7: 'dim7' };\n\n/**\n * The major seventh written after a base letter, which has taken the word's\n * place: `mMaj7` and `augMaj7` capitalize where a bare `maj7` cannot, since the\n * letter before it would otherwise read as the start of the word.\n */\nconst MAJOR_SEVENTH_AFTER_BASE = 'Maj7';\n\n/**\n * A core no quality name covers, written from its parts.\n *\n * A suspension carries its seventh in front of it the way a chart writes\n * `7sus4`, and a major base writes the seventh alone, since `maj` says nothing\n * a bare root does not. Both of those put the seventh at the front of the\n * suffix, where it is the lowercase word a chart writes — `maj7sus4`, not a\n * capitalized form that reads as a base letter.\n */\nfunction builtCoreSuffix(spec: ChordSpec): string {\n const base = BASE_SUFFIX[spec.base];\n if (spec.seventh === undefined) {\n return base;\n }\n const seventh = SEVENTH_SUFFIX[spec.seventh];\n if (spec.base === 'sus2' || spec.base === 'sus4') {\n return `${seventh}${base}`;\n }\n if (spec.base === 'maj') {\n return seventh;\n }\n return `${base}${spec.seventh === 'maj7' ? MAJOR_SEVENTH_AFTER_BASE : seventh}`;\n}\n\n/**\n * The figures a symbol writes after its core, in degree order.\n *\n * Whatever the core already names is left out of them, and so is an omitted\n * tone: a symbol names the harmony a player reads, and one that spelled out its\n * own omissions would not read as a chart's.\n */\nfunction figureText(spec: ChordSpec, core: ChordSpec | undefined): string[] {\n const figures: { degree: number; text: string }[] = [];\n for (const alteration of spec.alterations) {\n if (core !== undefined && carriesAlteration(core, alteration)) {\n continue;\n }\n const { degree, alter } = alteration;\n figures.push({ degree, text: `${alter < 0 ? 'b' : alter > 0 ? '#' : ''}${degree}` });\n }\n for (const degree of spec.additions) {\n if (core?.additions.includes(degree) !== true) {\n figures.push({ degree, text: `add${degree}` });\n }\n }\n return figures.sort((a, b) => a.degree - b.degree).map(({ text }) => text);\n}\n\n/**\n * The suffix a chord's structure is written as.\n *\n * A spec a quality name covers is written under that name, so every symbol a\n * chart already uses is unchanged. Anything else is written as the largest name\n * that fits inside it followed by the figures it does not cover — the way a\n * lead sheet writes tensions — so a combination no name covers still writes\n * itself out, and reads back.\n */\nfunction specSuffix(spec: ChordSpec): string {\n const named = exactChordSpecQuality(spec);\n if (named !== undefined) {\n return CANONICAL_SUFFIX[named];\n }\n const core = coreQuality(spec);\n const coreSpec = core === undefined ? undefined : chordSpecForQuality(core, 0);\n const text = core === undefined ? builtCoreSuffix(spec) : CANONICAL_SUFFIX[core];\n const figures = figureText(spec, coreSpec);\n return figures.length === 0 ? text : `${text}(${figures.join(',')})`;\n}\n\n/**\n * How a chord symbol is written: in which notation system, and on which side of\n * the enharmonic fence.\n *\n * @category Chords\n */\nexport type ChordSymbolOptions = NoteNameOptions & {\n /**\n * Prefer flat spellings over sharps for an altered root or bass; passing\n * either `true` or `false` overrides any spelling hint the chord carries.\n */\n flats?: boolean;\n};\n\n/**\n * Format a {@link Chord} as a lead-sheet chord symbol.\n *\n * The inverse of {@link parseChordSymbol} in every notation system: each\n * quality maps to one canonical suffix spelling, the root and bass are written\n * in `system`, and a slash bass is appended only when it differs from the root.\n * Every symbol written here reads back through {@link parseChordSymbol} as the\n * same root, sounding bass and tones — whatever combination of tensions the\n * chord carries, and whether or not a quality name covers it.\n * When the chord carries `rootSpelling`/`bassSpelling` hints (as chords from\n * {@link parseChordSymbol} do) and no explicit `flats` preference is given, the\n * hints are reused so flat symbols round-trip unchanged.\n *\n * The suffix is written from the chord's own tones (see {@link chordSpecOf}),\n * not from its quality name, so a chord carrying alterations no name covers is\n * written out in full — `C7(b9,#11)` — rather than under the nearest name. A\n * tone the chord leaves out is not written: the symbol names the harmony, so a\n * fifth-less dominant is still `C7`.\n *\n * @param chord The chord to format, as a chord symbol, chord data, or a\n * `Chord`.\n * @param opts `system` writes the root and bass in that notation system instead\n * of English; `flats` overrides the spelling hints, as\n * {@link ChordSymbolOptions} describes.\n * @returns The chord symbol text.\n * @example\n * ```ts\n * import { makeChord, formatChordSymbol, parseChordSymbol } from '@libraz/libcantus';\n * formatChordSymbol(makeChord(0, 'min7')); // 'Cm7'\n * formatChordSymbol(parseChordSymbol('Bb7'), { system: 'german' }); // 'B7'\n * formatChordSymbol(parseChordSymbol('B7'), { system: 'german' }); // 'H7'\n * ```\n * @category Chords\n */\nexport function formatChordSymbol(chord: ChordLike, opts?: ChordSymbolOptions): string {\n // A chord rebuilt from JSON or arrived at through arithmetic can carry a\n // non-finite root or a quality this module has no suffix for; formatting\n // those produces 'C' and 'Cundefined', which read as real symbols.\n const data = toChordData(chord);\n if (!Object.hasOwn(CANONICAL_SUFFIX, data.quality)) {\n throw new InvalidInputError(`Unknown chord quality: ${String(data.quality)}`);\n }\n const system = opts?.system ?? DEFAULT_SYSTEM;\n const rootPc = pitchClass(data.rootPc);\n const rootHint = data.rootSpelling;\n const rootName = pitchClassName(rootPc, rootHint, system, opts?.flats);\n const suffix = specSuffix(chordSpecOf(data));\n let symbol = `${rootName}${suffix}`;\n if (data.bassPc !== undefined && pitchClass(data.bassPc) !== rootPc) {\n const inheritFlats =\n rootHint !== undefined && noteToPitchClass(rootHint) === rootPc\n ? rootHint.alter < 0\n : undefined;\n const bassPc = pitchClass(data.bassPc);\n symbol += `/${pitchClassName(bassPc, data.bassSpelling, system, opts?.flats, inheritFlats)}`;\n }\n return symbol;\n}\n\n/**\n * Transpose a chord symbol by a number of semitones.\n *\n * The symbol is read in the same system it is written back in, so a German\n * chart stays German across the transposition.\n *\n * @param text The chord symbol text.\n * @param semitones Signed semitone offset to apply to the root and bass.\n * @param opts Options for reading and writing the symbol; see\n * {@link ChordSymbolOptions}.\n * @returns The transposed chord symbol text.\n * @throws If `text` does not parse as a chord symbol.\n * @example\n * ```ts\n * import { transposeChordSymbol } from '@libraz/libcantus';\n * transposeChordSymbol('C/G', 2); // 'D/A'\n * transposeChordSymbol('H7', 1, { system: 'german' }); // 'C7'\n * ```\n * @category Chords\n */\nexport function transposeChordSymbol(\n text: string,\n semitones: number,\n opts?: ChordSymbolOptions,\n): string {\n assertFiniteSemitones(semitones);\n const chord = parseChordSymbol(text, opts);\n const transposed: Chord = { ...chord, rootPc: pitchClass(chord.rootPc + semitones) };\n if (chord.rootSpelling !== undefined) {\n const hint = transposeNote(chord.rootSpelling, semitones);\n transposed.rootSpelling = { letter: hint.letter, alter: hint.alter };\n }\n if (chord.bassPc !== undefined) {\n transposed.bassPc = pitchClass(chord.bassPc + semitones);\n }\n if (chord.bassSpelling !== undefined) {\n const hint = transposeNote(chord.bassSpelling, semitones);\n transposed.bassSpelling = { letter: hint.letter, alter: hint.alter };\n }\n return formatChordSymbol(transposed, opts);\n}\n"],"mappings":"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","names":["hasMinorThird","NoteSafety","ReasonFlag","inScale","degree"]}
|