@libraz/libcantus 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +27 -65
  2. package/README_ja.md +28 -66
  3. package/dist/analyze/index.cjs +11 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +79 -144
  6. package/dist/analyze/index.d.ts +79 -144
  7. package/dist/analyze/index.js +20 -16
  8. package/dist/chunk-35NBV2FT.cjs +145 -0
  9. package/dist/chunk-35NBV2FT.cjs.map +1 -0
  10. package/dist/{chunk-UADYAYAT.cjs → chunk-3R3QVKY5.cjs} +1353 -580
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-WWURE4XT.cjs → chunk-76ZBTRG2.cjs} +624 -729
  13. package/dist/chunk-76ZBTRG2.cjs.map +1 -0
  14. package/dist/{chunk-FVSI36UV.js → chunk-7KG3RTYS.js} +197 -110
  15. package/dist/chunk-7KG3RTYS.js.map +1 -0
  16. package/dist/{chunk-VTI6FOLB.js → chunk-AGOVW7U6.js} +709 -133
  17. package/dist/chunk-AGOVW7U6.js.map +1 -0
  18. package/dist/{chunk-E3F4AHAH.cjs → chunk-CGBADIO5.cjs} +221 -134
  19. package/dist/chunk-CGBADIO5.cjs.map +1 -0
  20. package/dist/{chunk-6XH6QZYQ.cjs → chunk-DSMJRXCZ.cjs} +1455 -849
  21. package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
  22. package/dist/{chunk-PKFNUHDY.js → chunk-FBH75DAY.js} +1218 -612
  23. package/dist/chunk-FBH75DAY.js.map +1 -0
  24. package/dist/chunk-H2KJHBW4.js +3329 -0
  25. package/dist/chunk-H2KJHBW4.js.map +1 -0
  26. package/dist/chunk-HPVSH7SF.cjs +2294 -0
  27. package/dist/chunk-HPVSH7SF.cjs.map +1 -0
  28. package/dist/{chunk-MXO72PLY.cjs → chunk-HWHGOVO6.cjs} +21 -19
  29. package/dist/chunk-HWHGOVO6.cjs.map +1 -0
  30. package/dist/chunk-HYJJ2V2I.js +2294 -0
  31. package/dist/chunk-HYJJ2V2I.js.map +1 -0
  32. package/dist/{chunk-AFM2ULJH.cjs → chunk-J7FHVZII.cjs} +1567 -1067
  33. package/dist/chunk-J7FHVZII.cjs.map +1 -0
  34. package/dist/chunk-K6OZXIR6.js +1 -0
  35. package/dist/{chunk-H3VORETI.js → chunk-M5RYMAFI.js} +487 -592
  36. package/dist/chunk-M5RYMAFI.js.map +1 -0
  37. package/dist/chunk-NB25P3EH.js +363 -0
  38. package/dist/chunk-NB25P3EH.js.map +1 -0
  39. package/dist/{chunk-TTCLJX6D.js → chunk-NFJ4P6ZZ.js} +10 -8
  40. package/dist/chunk-NFJ4P6ZZ.js.map +1 -0
  41. package/dist/chunk-NM7UAMC3.cjs +457 -0
  42. package/dist/chunk-NM7UAMC3.cjs.map +1 -0
  43. package/dist/chunk-PM4XJE2Y.cjs +363 -0
  44. package/dist/chunk-PM4XJE2Y.cjs.map +1 -0
  45. package/dist/chunk-PUKJP2Z5.js +145 -0
  46. package/dist/chunk-PUKJP2Z5.js.map +1 -0
  47. package/dist/{chunk-VOHZB45S.js → chunk-Q6LFSAKV.js} +1321 -821
  48. package/dist/chunk-Q6LFSAKV.js.map +1 -0
  49. package/dist/{chunk-INHVXP6Z.js → chunk-RFIBF2RR.js} +1203 -430
  50. package/dist/chunk-RFIBF2RR.js.map +1 -0
  51. package/dist/{chunk-Q2WGKJNN.cjs → chunk-S5BKZZGM.cjs} +699 -123
  52. package/dist/chunk-S5BKZZGM.cjs.map +1 -0
  53. package/dist/chunk-SGLEQVJH.cjs +3329 -0
  54. package/dist/chunk-SGLEQVJH.cjs.map +1 -0
  55. package/dist/chunk-UGLFDG7L.cjs +1 -0
  56. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  57. package/dist/{chunk-YH3BIKHH.js → chunk-UMWZ4ZET.js} +146 -49
  58. package/dist/chunk-UMWZ4ZET.js.map +1 -0
  59. package/dist/{index-szBv3496.d.ts → coerce-DP0vYu-x.d.ts} +286 -199
  60. package/dist/{index-C_8o--VE.d.cts → coerce-DRERzdve.d.cts} +286 -199
  61. package/dist/core/index.cjs +9 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +122 -108
  64. package/dist/core/index.d.ts +122 -108
  65. package/dist/core/index.js +32 -28
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.d.cts +168 -84
  68. package/dist/generate/index.d.ts +168 -84
  69. package/dist/generate/index.js +7 -7
  70. package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
  71. package/dist/{index-CCNJRG-P.d.cts → index-B9QLziDe.d.cts} +293 -56
  72. package/dist/{index-Dzr13q3d.d.cts → index-BBWNZS0f.d.cts} +135 -58
  73. package/dist/{index-9wcMhG73.d.ts → index-BHIUyjhZ.d.ts} +293 -56
  74. package/dist/{index-BGFW-8P2.d.cts → index-BJgP97N1.d.ts} +196 -114
  75. package/dist/{index-CunYKLiu.d.ts → index-BL5N2Oyz.d.cts} +1 -1
  76. package/dist/{index-Cj49F892.d.ts → index-BMweUlm5.d.cts} +63 -23
  77. package/dist/{index-CgHqxdps.d.ts → index-BNSM-DSk.d.cts} +196 -114
  78. package/dist/{index-D57QRbVn.d.ts → index-BRMbPkHE.d.ts} +480 -254
  79. package/dist/{index-Chbst3Jx.d.ts → index-CJBBiAmU.d.ts} +135 -58
  80. package/dist/{index-BkGmlyI3.d.cts → index-CL4b4gUQ.d.ts} +63 -23
  81. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  82. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  83. package/dist/{index-BO6xE9YU.d.cts → index-DGCNKAUr.d.cts} +480 -254
  84. package/dist/{index-B4o55NDr.d.cts → index-Dw2DP7vm.d.ts} +1 -1
  85. package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
  86. package/dist/index.cjs +42 -14
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +14 -14
  89. package/dist/index.d.ts +14 -14
  90. package/dist/index.js +82 -54
  91. package/dist/{masks-DYbE485J.d.cts → masks-Cc7-MzzY.d.ts} +49 -2
  92. package/dist/{masks-DYbE485J.d.ts → masks-pADfTOiD.d.cts} +49 -2
  93. package/dist/model/index.cjs +19 -12
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +504 -182
  96. package/dist/model/index.d.ts +504 -182
  97. package/dist/model/index.js +18 -11
  98. package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
  99. package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
  100. package/dist/theory/index.cjs +35 -6
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +257 -62
  103. package/dist/theory/index.d.ts +257 -62
  104. package/dist/theory/index.js +41 -12
  105. package/package.json +7 -5
  106. package/dist/chunk-2LCPM3EG.js +0 -3224
  107. package/dist/chunk-2LCPM3EG.js.map +0 -1
  108. package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
  109. package/dist/chunk-6XPTBE7Y.cjs +0 -49
  110. package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
  111. package/dist/chunk-AFM2ULJH.cjs.map +0 -1
  112. package/dist/chunk-B6XNKWZV.cjs +0 -3224
  113. package/dist/chunk-B6XNKWZV.cjs.map +0 -1
  114. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  115. package/dist/chunk-ELCTZUJJ.js +0 -379
  116. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  117. package/dist/chunk-FVSI36UV.js.map +0 -1
  118. package/dist/chunk-H3VORETI.js.map +0 -1
  119. package/dist/chunk-H42UHPYW.js +0 -1577
  120. package/dist/chunk-H42UHPYW.js.map +0 -1
  121. package/dist/chunk-HFPS7JFD.js +0 -1
  122. package/dist/chunk-INHVXP6Z.js.map +0 -1
  123. package/dist/chunk-K4ZJUGF4.cjs +0 -379
  124. package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
  125. package/dist/chunk-KNJEKLKV.js +0 -49
  126. package/dist/chunk-KNJEKLKV.js.map +0 -1
  127. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  128. package/dist/chunk-NLEU4QSE.cjs +0 -360
  129. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  130. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  131. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  132. package/dist/chunk-PKFNUHDY.js.map +0 -1
  133. package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
  134. package/dist/chunk-RHIJ3S4I.cjs +0 -1
  135. package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
  136. package/dist/chunk-TTCLJX6D.js.map +0 -1
  137. package/dist/chunk-UADYAYAT.cjs.map +0 -1
  138. package/dist/chunk-VOHZB45S.js.map +0 -1
  139. package/dist/chunk-VTI6FOLB.js.map +0 -1
  140. package/dist/chunk-WWURE4XT.cjs.map +0 -1
  141. package/dist/chunk-YH3BIKHH.js.map +0 -1
  142. /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
@@ -315,13 +315,18 @@ type SpelledInterval = {
315
315
  /** Signed semitone distance from the first note to the second. */
316
316
  semitones: number;
317
317
  /**
318
- * True when the diatonic letters move down, and absent otherwise — an
319
- * ascending interval leaves the key out rather than carrying `false`, so
320
- * every producer of this type returns the same object for the same interval.
321
- * The flag is not redundant with the sign of the span: a descending unison
322
- * spans zero semitones, and a letter can rise while the pitch falls.
318
+ * Whether the diatonic letters move down.
319
+ *
320
+ * Always present, so the direction of a written-down interval is not
321
+ * something a reader can lose: data rebuilt without the key would otherwise
322
+ * come back ascending with nothing to say it had changed. Every producer
323
+ * decides it, and the compiler asks each one to.
324
+ *
325
+ * Not redundant with the sign of the span: a descending unison spans zero
326
+ * semitones, and a letter can rise while the pitch falls — `dd2` climbs a
327
+ * letter and loses a semitone.
323
328
  */
324
- descending?: boolean;
329
+ descending: boolean;
325
330
  };
326
331
  /**
327
332
  * Reduce a MIDI pitch, a bare pitch class, or any signed offset to a pitch
@@ -599,7 +604,11 @@ declare function transposeNote(note: Note, semitones: number, opts?: {
599
604
  *
600
605
  * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
601
606
  * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
602
- * @returns The unsigned semitone span.
607
+ * @returns The signed semitone span the name covers when its letters ascend.
608
+ * Nearly every name spans upward, but a second narrowed far enough crosses
609
+ * zero while its letters still rise — C# up to Dbb is a doubly diminished
610
+ * second spanning -1 — and the sign is what keeps that name reading as the
611
+ * same interval a measurement of those two notes reports.
603
612
  * @throws If the quality cannot apply to the number, such as a major fifth or a
604
613
  * diminished unison.
605
614
  * @example
@@ -620,15 +629,16 @@ declare function intervalSemitones(numberValue: number, quality: IntervalQuality
620
629
  *
621
630
  * @param name The interval name: an optional `'-'`, a quality label, and a
622
631
  * diatonic number. Surrounding whitespace is ignored.
623
- * @returns The spelled interval. An ascending name carries a non-negative span
624
- * and no `descending` flag.
632
+ * @returns The spelled interval. `descending` is always present, `false` for an
633
+ * ascending name, and the span is the one {@link intervalSemitones} reports —
634
+ * negative for the rare second narrowed past a zero span.
625
635
  * @throws If the name is not a quality label followed by a number, or the two
626
636
  * cannot describe the same interval. Use {@link tryParseInterval} where
627
637
  * failure is ordinary, such as an interval field read on every keystroke.
628
638
  * @example
629
639
  * ```ts
630
640
  * import { parseInterval } from '@libraz/libcantus';
631
- * parseInterval('m3'); // { number: 3, quality: 'm', semitones: 3 }
641
+ * parseInterval('m3'); // { number: 3, quality: 'm', semitones: 3, descending: false }
632
642
  * parseInterval('-m3'); // { number: 3, quality: 'm', semitones: -3, descending: true }
633
643
  * ```
634
644
  * @category Pitch & Intervals
@@ -659,9 +669,23 @@ declare function tryParseInterval(name: string): ParseResult<SpelledInterval>;
659
669
  *
660
670
  * @category Pitch & Intervals
661
671
  */
662
- type IntervalLike = string | SpelledInterval | {
672
+ type IntervalLike = string | IntervalData | {
663
673
  /** The interval data this value stands for. */
664
- toJSON(): SpelledInterval;
674
+ toJSON(): IntervalData;
675
+ };
676
+ /**
677
+ * Interval data as a caller may write it, with the direction optional.
678
+ *
679
+ * Deliberately wider than {@link SpelledInterval}: a caller hands in what they
680
+ * have, including data written before the direction travelled with it, and
681
+ * {@link toSpelledInterval} decides what is missing. Narrowing the input to the
682
+ * resolved shape would refuse the very data the resolver exists to repair.
683
+ *
684
+ * @category Pitch & Intervals
685
+ */
686
+ type IntervalData = Omit<SpelledInterval, 'descending'> & {
687
+ /** Whether the letters move down; read from the span and quality when absent. */
688
+ descending?: boolean;
665
689
  };
666
690
  /**
667
691
  * Resolve any interval-shaped value to plain {@link SpelledInterval} data.
@@ -674,14 +698,14 @@ type IntervalLike = string | SpelledInterval | {
674
698
  *
675
699
  * @param value An interval name, plain interval data, or a value whose `toJSON`
676
700
  * returns interval data.
677
- * @returns The validated interval data, carrying `descending` only when the
678
- * interval descends.
701
+ * @returns The validated interval data, always carrying `descending`, `false`
702
+ * for an interval that ascends.
679
703
  * @throws If the value is not interval-shaped, or its number, quality, and span
680
704
  * do not describe the same interval.
681
705
  * @example
682
706
  * ```ts
683
707
  * import { toSpelledInterval } from '@libraz/libcantus';
684
- * toSpelledInterval('P5'); // { number: 5, quality: 'P', semitones: 7 }
708
+ * toSpelledInterval('P5'); // { number: 5, quality: 'P', semitones: 7, descending: false }
685
709
  * toSpelledInterval({ number: 5, quality: 'P', semitones: -7 });
686
710
  * // { number: 5, quality: 'P', semitones: -7, descending: true }
687
711
  * ```
@@ -736,17 +760,23 @@ declare function toNoteData(value: NoteLike): Note;
736
760
  * the diatonic number decides the letter here: transposing C by an augmented
737
761
  * second gives D#, not Eb.
738
762
  *
739
- * @param note The note to transpose.
740
- * @param interval The interval to apply; a negative span transposes downward.
763
+ * Both arguments are taken in whatever form the caller holds them — the text a
764
+ * field carries, the plain data the library returns, or the class that wraps
765
+ * it — as the class mirror of this operation already did.
766
+ *
767
+ * @param given The note to transpose: a name, a MIDI number, plain note data,
768
+ * or a `Note`.
769
+ * @param named The interval to apply: a name, plain interval data, or an
770
+ * `Interval`. A negative span transposes downward.
741
771
  * @returns The transposed note, octave-less if the source was.
742
772
  * @example
743
773
  * ```ts
744
- * import { formatNote, parseInterval, parseNote, transposeByInterval } from '@libraz/libcantus';
745
- * formatNote(transposeByInterval(parseNote('C4'), parseInterval('A2'))); // 'D#4'
774
+ * import { formatNote, transposeByInterval } from '@libraz/libcantus';
775
+ * formatNote(transposeByInterval('C4', 'A2')); // 'D#4'
746
776
  * ```
747
777
  * @category Pitch & Intervals
748
778
  */
749
- declare function transposeByInterval(note: Note, interval: SpelledInterval): Note;
779
+ declare function transposeByInterval(given: NoteLike, named: IntervalLike): Note;
750
780
  /**
751
781
  * The spelled interval from one note to another.
752
782
  *
@@ -761,10 +791,10 @@ declare function transposeByInterval(note: Note, interval: SpelledInterval): Not
761
791
  * ```ts
762
792
  * import { spelledInterval, parseNote } from '@libraz/libcantus';
763
793
  * spelledInterval(parseNote('C4'), parseNote('G4'));
764
- * // { number: 5, quality: 'P', semitones: 7 }
794
+ * // { number: 5, quality: 'P', semitones: 7, descending: false }
765
795
  * ```
766
796
  * @category Pitch & Intervals
767
797
  */
768
798
  declare function spelledInterval(a: Note, b: Note): SpelledInterval;
769
799
 
770
- export { ARTICULATIONS as A, BudgetExceededError as B, toSpelledInterval as C, transposeByInterval as D, transposeNote as E, tryParseInterval as F, tryParseKeyName as G, tryParseNote as H, type IntervalLike as I, type KeyName as K, type LibcantusError as L, NoSolutionError as N, type ParseResult as P, type SpelledInterval as S, type Articulation as a, type IntervalQualityLabel as b, InvalidInputError as c, type KeyScale as d, type LibcantusErrorCode as e, type Note as f, type NoteEvent as g, type NoteLike as h, type NoteNameOptions as i, type NoteNameSystem as j, detectNoteNameSystem as k, diatonicLetterOf as l, formatKeyName as m, formatNote as n, intervalSemitones as o, isLibcantusError as p, midiToNote as q, naturalPitchClassOf as r, noteToMidi as s, noteToPitchClass as t, parseInterval as u, parseKeyName as v, parseNote as w, pitchClassOf as x, spelledInterval as y, toNoteData as z };
800
+ export { ARTICULATIONS as A, BudgetExceededError as B, toNoteData as C, toSpelledInterval as D, transposeByInterval as E, transposeNote as F, tryParseInterval as G, tryParseKeyName as H, type IntervalData as I, tryParseNote as J, type KeyName as K, type LibcantusError as L, NoSolutionError as N, type ParseResult as P, type SpelledInterval as S, type Articulation as a, type IntervalLike as b, type IntervalQualityLabel as c, InvalidInputError as d, type KeyScale as e, type LibcantusErrorCode as f, type Note as g, type NoteEvent as h, type NoteLike as i, type NoteNameOptions as j, type NoteNameSystem as k, detectNoteNameSystem as l, diatonicLetterOf as m, formatKeyName as n, formatNote as o, intervalSemitones as p, isLibcantusError as q, midiToNote as r, naturalPitchClassOf as s, noteToMidi as t, noteToPitchClass as u, parseInterval as v, parseKeyName as w, parseNote as x, pitchClassOf as y, spelledInterval as z };
@@ -315,13 +315,18 @@ type SpelledInterval = {
315
315
  /** Signed semitone distance from the first note to the second. */
316
316
  semitones: number;
317
317
  /**
318
- * True when the diatonic letters move down, and absent otherwise — an
319
- * ascending interval leaves the key out rather than carrying `false`, so
320
- * every producer of this type returns the same object for the same interval.
321
- * The flag is not redundant with the sign of the span: a descending unison
322
- * spans zero semitones, and a letter can rise while the pitch falls.
318
+ * Whether the diatonic letters move down.
319
+ *
320
+ * Always present, so the direction of a written-down interval is not
321
+ * something a reader can lose: data rebuilt without the key would otherwise
322
+ * come back ascending with nothing to say it had changed. Every producer
323
+ * decides it, and the compiler asks each one to.
324
+ *
325
+ * Not redundant with the sign of the span: a descending unison spans zero
326
+ * semitones, and a letter can rise while the pitch falls — `dd2` climbs a
327
+ * letter and loses a semitone.
323
328
  */
324
- descending?: boolean;
329
+ descending: boolean;
325
330
  };
326
331
  /**
327
332
  * Reduce a MIDI pitch, a bare pitch class, or any signed offset to a pitch
@@ -599,7 +604,11 @@ declare function transposeNote(note: Note, semitones: number, opts?: {
599
604
  *
600
605
  * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
601
606
  * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
602
- * @returns The unsigned semitone span.
607
+ * @returns The signed semitone span the name covers when its letters ascend.
608
+ * Nearly every name spans upward, but a second narrowed far enough crosses
609
+ * zero while its letters still rise — C# up to Dbb is a doubly diminished
610
+ * second spanning -1 — and the sign is what keeps that name reading as the
611
+ * same interval a measurement of those two notes reports.
603
612
  * @throws If the quality cannot apply to the number, such as a major fifth or a
604
613
  * diminished unison.
605
614
  * @example
@@ -620,15 +629,16 @@ declare function intervalSemitones(numberValue: number, quality: IntervalQuality
620
629
  *
621
630
  * @param name The interval name: an optional `'-'`, a quality label, and a
622
631
  * diatonic number. Surrounding whitespace is ignored.
623
- * @returns The spelled interval. An ascending name carries a non-negative span
624
- * and no `descending` flag.
632
+ * @returns The spelled interval. `descending` is always present, `false` for an
633
+ * ascending name, and the span is the one {@link intervalSemitones} reports —
634
+ * negative for the rare second narrowed past a zero span.
625
635
  * @throws If the name is not a quality label followed by a number, or the two
626
636
  * cannot describe the same interval. Use {@link tryParseInterval} where
627
637
  * failure is ordinary, such as an interval field read on every keystroke.
628
638
  * @example
629
639
  * ```ts
630
640
  * import { parseInterval } from '@libraz/libcantus';
631
- * parseInterval('m3'); // { number: 3, quality: 'm', semitones: 3 }
641
+ * parseInterval('m3'); // { number: 3, quality: 'm', semitones: 3, descending: false }
632
642
  * parseInterval('-m3'); // { number: 3, quality: 'm', semitones: -3, descending: true }
633
643
  * ```
634
644
  * @category Pitch & Intervals
@@ -659,9 +669,23 @@ declare function tryParseInterval(name: string): ParseResult<SpelledInterval>;
659
669
  *
660
670
  * @category Pitch & Intervals
661
671
  */
662
- type IntervalLike = string | SpelledInterval | {
672
+ type IntervalLike = string | IntervalData | {
663
673
  /** The interval data this value stands for. */
664
- toJSON(): SpelledInterval;
674
+ toJSON(): IntervalData;
675
+ };
676
+ /**
677
+ * Interval data as a caller may write it, with the direction optional.
678
+ *
679
+ * Deliberately wider than {@link SpelledInterval}: a caller hands in what they
680
+ * have, including data written before the direction travelled with it, and
681
+ * {@link toSpelledInterval} decides what is missing. Narrowing the input to the
682
+ * resolved shape would refuse the very data the resolver exists to repair.
683
+ *
684
+ * @category Pitch & Intervals
685
+ */
686
+ type IntervalData = Omit<SpelledInterval, 'descending'> & {
687
+ /** Whether the letters move down; read from the span and quality when absent. */
688
+ descending?: boolean;
665
689
  };
666
690
  /**
667
691
  * Resolve any interval-shaped value to plain {@link SpelledInterval} data.
@@ -674,14 +698,14 @@ type IntervalLike = string | SpelledInterval | {
674
698
  *
675
699
  * @param value An interval name, plain interval data, or a value whose `toJSON`
676
700
  * returns interval data.
677
- * @returns The validated interval data, carrying `descending` only when the
678
- * interval descends.
701
+ * @returns The validated interval data, always carrying `descending`, `false`
702
+ * for an interval that ascends.
679
703
  * @throws If the value is not interval-shaped, or its number, quality, and span
680
704
  * do not describe the same interval.
681
705
  * @example
682
706
  * ```ts
683
707
  * import { toSpelledInterval } from '@libraz/libcantus';
684
- * toSpelledInterval('P5'); // { number: 5, quality: 'P', semitones: 7 }
708
+ * toSpelledInterval('P5'); // { number: 5, quality: 'P', semitones: 7, descending: false }
685
709
  * toSpelledInterval({ number: 5, quality: 'P', semitones: -7 });
686
710
  * // { number: 5, quality: 'P', semitones: -7, descending: true }
687
711
  * ```
@@ -736,17 +760,23 @@ declare function toNoteData(value: NoteLike): Note;
736
760
  * the diatonic number decides the letter here: transposing C by an augmented
737
761
  * second gives D#, not Eb.
738
762
  *
739
- * @param note The note to transpose.
740
- * @param interval The interval to apply; a negative span transposes downward.
763
+ * Both arguments are taken in whatever form the caller holds them — the text a
764
+ * field carries, the plain data the library returns, or the class that wraps
765
+ * it — as the class mirror of this operation already did.
766
+ *
767
+ * @param given The note to transpose: a name, a MIDI number, plain note data,
768
+ * or a `Note`.
769
+ * @param named The interval to apply: a name, plain interval data, or an
770
+ * `Interval`. A negative span transposes downward.
741
771
  * @returns The transposed note, octave-less if the source was.
742
772
  * @example
743
773
  * ```ts
744
- * import { formatNote, parseInterval, parseNote, transposeByInterval } from '@libraz/libcantus';
745
- * formatNote(transposeByInterval(parseNote('C4'), parseInterval('A2'))); // 'D#4'
774
+ * import { formatNote, transposeByInterval } from '@libraz/libcantus';
775
+ * formatNote(transposeByInterval('C4', 'A2')); // 'D#4'
746
776
  * ```
747
777
  * @category Pitch & Intervals
748
778
  */
749
- declare function transposeByInterval(note: Note, interval: SpelledInterval): Note;
779
+ declare function transposeByInterval(given: NoteLike, named: IntervalLike): Note;
750
780
  /**
751
781
  * The spelled interval from one note to another.
752
782
  *
@@ -761,10 +791,10 @@ declare function transposeByInterval(note: Note, interval: SpelledInterval): Not
761
791
  * ```ts
762
792
  * import { spelledInterval, parseNote } from '@libraz/libcantus';
763
793
  * spelledInterval(parseNote('C4'), parseNote('G4'));
764
- * // { number: 5, quality: 'P', semitones: 7 }
794
+ * // { number: 5, quality: 'P', semitones: 7, descending: false }
765
795
  * ```
766
796
  * @category Pitch & Intervals
767
797
  */
768
798
  declare function spelledInterval(a: Note, b: Note): SpelledInterval;
769
799
 
770
- export { ARTICULATIONS as A, BudgetExceededError as B, toSpelledInterval as C, transposeByInterval as D, transposeNote as E, tryParseInterval as F, tryParseKeyName as G, tryParseNote as H, type IntervalLike as I, type KeyName as K, type LibcantusError as L, NoSolutionError as N, type ParseResult as P, type SpelledInterval as S, type Articulation as a, type IntervalQualityLabel as b, InvalidInputError as c, type KeyScale as d, type LibcantusErrorCode as e, type Note as f, type NoteEvent as g, type NoteLike as h, type NoteNameOptions as i, type NoteNameSystem as j, detectNoteNameSystem as k, diatonicLetterOf as l, formatKeyName as m, formatNote as n, intervalSemitones as o, isLibcantusError as p, midiToNote as q, naturalPitchClassOf as r, noteToMidi as s, noteToPitchClass as t, parseInterval as u, parseKeyName as v, parseNote as w, pitchClassOf as x, spelledInterval as y, toNoteData as z };
800
+ export { ARTICULATIONS as A, BudgetExceededError as B, toNoteData as C, toSpelledInterval as D, transposeByInterval as E, transposeNote as F, tryParseInterval as G, tryParseKeyName as H, type IntervalData as I, tryParseNote as J, type KeyName as K, type LibcantusError as L, NoSolutionError as N, type ParseResult as P, type SpelledInterval as S, type Articulation as a, type IntervalLike as b, type IntervalQualityLabel as c, InvalidInputError as d, type KeyScale as e, type LibcantusErrorCode as f, type Note as g, type NoteEvent as h, type NoteLike as i, type NoteNameOptions as j, type NoteNameSystem as k, detectNoteNameSystem as l, diatonicLetterOf as m, formatKeyName as n, formatNote as o, intervalSemitones as p, isLibcantusError as q, midiToNote as r, naturalPitchClassOf as s, noteToMidi as t, noteToPitchClass as u, parseInterval as v, parseKeyName as w, parseNote as x, pitchClassOf as y, spelledInterval as z };