@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.
Files changed (186) hide show
  1. package/README.md +61 -261
  2. package/README_ja.md +115 -0
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +213 -456
  6. package/dist/analyze/index.d.ts +213 -456
  7. package/dist/analyze/index.js +56 -9
  8. package/dist/chunk-3JXCW6DX.cjs +3220 -0
  9. package/dist/chunk-3JXCW6DX.cjs.map +1 -0
  10. package/dist/chunk-4A2RMUCC.js +7647 -0
  11. package/dist/chunk-4A2RMUCC.js.map +1 -0
  12. package/dist/chunk-6XPTBE7Y.cjs +49 -0
  13. package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
  14. package/dist/chunk-AFM2ULJH.cjs +5121 -0
  15. package/dist/chunk-AFM2ULJH.cjs.map +1 -0
  16. package/dist/chunk-E3F4AHAH.cjs +690 -0
  17. package/dist/chunk-E3F4AHAH.cjs.map +1 -0
  18. package/dist/chunk-ELCTZUJJ.js +379 -0
  19. package/dist/chunk-ELCTZUJJ.js.map +1 -0
  20. package/dist/chunk-FVSI36UV.js +690 -0
  21. package/dist/chunk-FVSI36UV.js.map +1 -0
  22. package/dist/chunk-GEDTJHAC.cjs +1 -0
  23. package/dist/chunk-GEDTJHAC.cjs.map +1 -0
  24. package/dist/chunk-GOZG57ER.js +3220 -0
  25. package/dist/chunk-GOZG57ER.js.map +1 -0
  26. package/dist/chunk-H3VORETI.js +2243 -0
  27. package/dist/chunk-H3VORETI.js.map +1 -0
  28. package/dist/chunk-H42UHPYW.js +1577 -0
  29. package/dist/chunk-H42UHPYW.js.map +1 -0
  30. package/dist/chunk-HFPS7JFD.js +1 -0
  31. package/dist/chunk-INHVXP6Z.js +3053 -0
  32. package/dist/chunk-INHVXP6Z.js.map +1 -0
  33. package/dist/chunk-K4ZJUGF4.cjs +379 -0
  34. package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
  35. package/dist/chunk-KNJEKLKV.js +49 -0
  36. package/dist/chunk-KNJEKLKV.js.map +1 -0
  37. package/dist/chunk-M4CWTJWJ.cjs +7647 -0
  38. package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
  39. package/dist/chunk-MXO72PLY.cjs +211 -0
  40. package/dist/chunk-MXO72PLY.cjs.map +1 -0
  41. package/dist/chunk-NLEU4QSE.cjs +360 -0
  42. package/dist/chunk-NLEU4QSE.cjs.map +1 -0
  43. package/dist/chunk-OE6WK2Y2.cjs +1577 -0
  44. package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
  45. package/dist/chunk-Q2WGKJNN.cjs +1064 -0
  46. package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
  47. package/dist/chunk-QYQQ5J6E.js +1 -0
  48. package/dist/chunk-QYQQ5J6E.js.map +1 -0
  49. package/dist/chunk-RHIJ3S4I.cjs +1 -0
  50. package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
  51. package/dist/chunk-TTCLJX6D.js +211 -0
  52. package/dist/chunk-TTCLJX6D.js.map +1 -0
  53. package/dist/chunk-UADYAYAT.cjs +3053 -0
  54. package/dist/chunk-UADYAYAT.cjs.map +1 -0
  55. package/dist/chunk-VOHZB45S.js +5121 -0
  56. package/dist/chunk-VOHZB45S.js.map +1 -0
  57. package/dist/chunk-VTI6FOLB.js +1064 -0
  58. package/dist/chunk-VTI6FOLB.js.map +1 -0
  59. package/dist/chunk-WWURE4XT.cjs +2243 -0
  60. package/dist/chunk-WWURE4XT.cjs.map +1 -0
  61. package/dist/chunk-YH3BIKHH.js +360 -0
  62. package/dist/chunk-YH3BIKHH.js.map +1 -0
  63. package/dist/core/index.cjs +103 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +96 -345
  66. package/dist/core/index.d.ts +96 -345
  67. package/dist/core/index.js +104 -7
  68. package/dist/generate/index.cjs +89 -10
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +437 -1095
  71. package/dist/generate/index.d.ts +437 -1095
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
  75. package/dist/index-B9MNZboQ.d.cts +770 -0
  76. package/dist/index-B9MNZboQ.d.ts +770 -0
  77. package/dist/index-BGFW-8P2.d.cts +2307 -0
  78. package/dist/index-BO6xE9YU.d.cts +1804 -0
  79. package/dist/index-BkGmlyI3.d.cts +470 -0
  80. package/dist/index-CCNJRG-P.d.cts +1095 -0
  81. package/dist/index-CGpRv5sl.d.cts +643 -0
  82. package/dist/index-C_8o--VE.d.cts +1242 -0
  83. package/dist/index-CgHqxdps.d.ts +2307 -0
  84. package/dist/index-Chbst3Jx.d.ts +1343 -0
  85. package/dist/index-Cj49F892.d.ts +470 -0
  86. package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
  87. package/dist/index-D57QRbVn.d.ts +1804 -0
  88. package/dist/index-DInQqT8Q.d.cts +66 -0
  89. package/dist/index-DInQqT8Q.d.ts +66 -0
  90. package/dist/index-Dzr13q3d.d.cts +1343 -0
  91. package/dist/index-G1uwGGWQ.d.ts +643 -0
  92. package/dist/index-szBv3496.d.ts +1242 -0
  93. package/dist/index.cjs +358 -15
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +17 -15
  96. package/dist/index.d.ts +17 -15
  97. package/dist/index.js +407 -64
  98. package/dist/masks-DYbE485J.d.cts +445 -0
  99. package/dist/masks-DYbE485J.d.ts +445 -0
  100. package/dist/model/index.cjs +39 -7
  101. package/dist/model/index.cjs.map +1 -1
  102. package/dist/model/index.d.cts +4685 -243
  103. package/dist/model/index.d.ts +4685 -243
  104. package/dist/model/index.js +40 -8
  105. package/dist/positional-Cptlj-xs.d.ts +287 -0
  106. package/dist/positional-DvH_sd8u.d.cts +287 -0
  107. package/dist/theory/index.cjs +101 -7
  108. package/dist/theory/index.cjs.map +1 -1
  109. package/dist/theory/index.d.cts +513 -173
  110. package/dist/theory/index.d.ts +513 -173
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +16 -11
  113. package/dist/chunk-36N73KZ3.cjs +0 -246
  114. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  115. package/dist/chunk-4HLLZPXT.cjs +0 -3077
  116. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  117. package/dist/chunk-7MGZ7D62.cjs +0 -498
  118. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  119. package/dist/chunk-7NKDPF74.js +0 -3077
  120. package/dist/chunk-7NKDPF74.js.map +0 -1
  121. package/dist/chunk-CAN76NCU.cjs +0 -679
  122. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  123. package/dist/chunk-D2FAJBYL.js +0 -479
  124. package/dist/chunk-D2FAJBYL.js.map +0 -1
  125. package/dist/chunk-EHVEBWM5.js +0 -1449
  126. package/dist/chunk-EHVEBWM5.js.map +0 -1
  127. package/dist/chunk-FCANXBAT.cjs +0 -30
  128. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  129. package/dist/chunk-FNDWXRRI.cjs +0 -479
  130. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  131. package/dist/chunk-GQSGS2N4.js +0 -173
  132. package/dist/chunk-GQSGS2N4.js.map +0 -1
  133. package/dist/chunk-HKGYCQ34.js +0 -53
  134. package/dist/chunk-HKGYCQ34.js.map +0 -1
  135. package/dist/chunk-I24MV65U.js +0 -106
  136. package/dist/chunk-I24MV65U.js.map +0 -1
  137. package/dist/chunk-KMHOICIY.js +0 -679
  138. package/dist/chunk-KMHOICIY.js.map +0 -1
  139. package/dist/chunk-LW53AHQU.cjs +0 -1117
  140. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  141. package/dist/chunk-N6IHQUOX.js +0 -1
  142. package/dist/chunk-NKIBQ6PT.js +0 -30
  143. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  144. package/dist/chunk-NP4NA2FB.cjs +0 -106
  145. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  146. package/dist/chunk-NVE34YGZ.cjs +0 -1449
  147. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  148. package/dist/chunk-NYYWMV2C.js +0 -327
  149. package/dist/chunk-NYYWMV2C.js.map +0 -1
  150. package/dist/chunk-Q2XUP227.js +0 -845
  151. package/dist/chunk-Q2XUP227.js.map +0 -1
  152. package/dist/chunk-Q6OP4U43.cjs +0 -327
  153. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  154. package/dist/chunk-RC6GBMCD.cjs +0 -845
  155. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  156. package/dist/chunk-RK35RIN7.js +0 -1117
  157. package/dist/chunk-RK35RIN7.js.map +0 -1
  158. package/dist/chunk-RUMU3UA6.cjs +0 -53
  159. package/dist/chunk-RUMU3UA6.cjs.map +0 -1
  160. package/dist/chunk-T64C7ZD7.cjs +0 -1
  161. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  162. package/dist/chunk-XEBG3G53.js +0 -498
  163. package/dist/chunk-XEBG3G53.js.map +0 -1
  164. package/dist/chunk-YLEMGEFH.js +0 -246
  165. package/dist/chunk-YLEMGEFH.js.map +0 -1
  166. package/dist/chunk-ZEJR4TKL.cjs +0 -173
  167. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  168. package/dist/function-BtiZb9i9.d.ts +0 -208
  169. package/dist/function-DNugAOrt.d.cts +0 -208
  170. package/dist/index-6VR8-kit.d.ts +0 -143
  171. package/dist/index-70p168VM.d.ts +0 -192
  172. package/dist/index-Be74mcon.d.cts +0 -152
  173. package/dist/index-BtXT6UQ0.d.cts +0 -192
  174. package/dist/index-C5UBbQ4G.d.ts +0 -152
  175. package/dist/index-C83mZI1P.d.ts +0 -242
  176. package/dist/index-DXd9JikN.d.cts +0 -246
  177. package/dist/index-DXd9JikN.d.ts +0 -246
  178. package/dist/index-OG65ocOr.d.cts +0 -272
  179. package/dist/index-OG65ocOr.d.ts +0 -272
  180. package/dist/index-tJhkmQgE.d.cts +0 -143
  181. package/dist/index-y_ak9_Hi.d.cts +0 -242
  182. package/dist/styled-DIRkk9Uz.d.ts +0 -506
  183. package/dist/styled-DJbkJQfj.d.cts +0 -506
  184. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  185. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  186. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -1,246 +0,0 @@
1
- /**
2
- * Meter and time signatures: bar/beat arithmetic, simple vs. compound meters,
3
- * metric-accent hierarchy, and tuplet subdivision.
4
- *
5
- * Positions and durations are measured in quarter-note beats, matching the rest
6
- * of the library's beat convention (four quarter-note beats per bar in 4/4).
7
- */
8
- /**
9
- * A time signature as a numerator over a note-value denominator.
10
- *
11
- * @category Rhythm & Meter
12
- */
13
- type TimeSignature = {
14
- numerator: number;
15
- denominator: number;
16
- /**
17
- * Optional additive grouping of the bar's main pulses, as the felt-beat
18
- * lengths in pulses — e.g. `[2, 2, 3]` for a 2+2+3 reading of 7/8, or
19
- * `[3, 2]` for 5/8. The entries must be positive integers summing to the
20
- * numerator.
21
- *
22
- * A compound numerator accepts both readings. 9/8 groups its three dotted
23
- * pulses as `[1, 1, 1]`, or reads additively as `[2, 2, 2, 3]` quavers; a
24
- * grouping that sums to the numerator selects the additive reading, so
25
- * aksak and other additive metres are expressible. {@link pulsesPerBar}
26
- * and {@link isCompound} follow whichever reading the grouping selects.
27
- *
28
- * When present, the head pulse of each group (other than the downbeat) is
29
- * treated as a secondary strong pulse by
30
- * {@link metricWeight}/{@link isStrongBeat}; when absent, all main pulses
31
- * are weighted equally as flat, evenly divided pulses.
32
- */
33
- grouping?: number[];
34
- };
35
- /**
36
- * A position expressed as a bar index and a quarter-note offset within the bar.
37
- *
38
- * @category Rhythm & Meter
39
- */
40
- type BarPosition = {
41
- /** 0-based bar index. {@link formatBarPosition} renders it 1-based. */
42
- bar: number;
43
- /**
44
- * Quarter-note offset from the start of the bar — not the felt beat. In 6/8
45
- * the second felt beat is at `beat: 1.5`; {@link barPositionToPulse} converts
46
- * to felt-beat numbering.
47
- */
48
- beat: number;
49
- };
50
- /**
51
- * Parse a time signature such as `"4/4"` or `"6/8"`.
52
- *
53
- * @param text The signature text.
54
- * @returns The parsed time signature.
55
- * @throws If the text is not `n/d` with positive integers.
56
- * @example
57
- * ```ts
58
- * import { parseTimeSignature } from '@libraz/libcantus';
59
- * parseTimeSignature('6/8'); // { numerator: 6, denominator: 8 }
60
- * ```
61
- * @category Rhythm & Meter
62
- */
63
- declare function parseTimeSignature(text: string): TimeSignature;
64
- /**
65
- * Render a time signature as `"n/d"`, or as `"a+b+c/d"` when it carries an
66
- * additive grouping and `grouping` is requested.
67
- *
68
- * The plain form drops the grouping, so a round trip through it reads 7/8 as
69
- * flat rather than as 2+2+3; ask for the additive form to keep it.
70
- *
71
- * @param ts The time signature.
72
- * @param opts Set `grouping: true` to render an additive grouping.
73
- * @returns The formatted signature.
74
- * @example
75
- * ```ts
76
- * import { formatTimeSignature } from '@libraz/libcantus';
77
- * const aksak = { numerator: 7, denominator: 8, grouping: [2, 2, 3] };
78
- * formatTimeSignature(aksak); // '7/8'
79
- * formatTimeSignature(aksak, { grouping: true }); // '2+2+3/8'
80
- * ```
81
- * @category Rhythm & Meter
82
- */
83
- declare function formatTimeSignature(ts: TimeSignature, opts?: {
84
- grouping?: boolean;
85
- }): string;
86
- /**
87
- * Whether a meter is compound: its main pulses each divide into three, as in
88
- * 6/8, 9/8, 12/8, or 6/4. Compound meters are those whose numerator is a
89
- * multiple of three greater than three, independent of the denominator. Meters
90
- * like 3/8 and 3/4 (simple triples, numerator 3) are not compound, and neither
91
- * is a signature whose {@link TimeSignature.grouping} selects the additive
92
- * reading (9/8 as 2+2+2+3).
93
- *
94
- * @param ts The time signature.
95
- * @returns True for compound meters.
96
- * @category Rhythm & Meter
97
- */
98
- declare function isCompound(ts: TimeSignature): boolean;
99
- /**
100
- * Length of a bar in quarter-note beats.
101
- *
102
- * @param ts The time signature.
103
- * @returns The bar length in quarter notes.
104
- * @category Rhythm & Meter
105
- */
106
- declare function beatsPerBar(ts: TimeSignature): number;
107
- /**
108
- * Number of main pulses (felt beats) per bar: the numerator for simple meters,
109
- * a third of it for compound meters.
110
- *
111
- * For additive/irregular meters such as 7/8 or 5/8 this counts the raw pulses
112
- * (7 and 5 respectively), which are otherwise felt as flat, evenly divided and
113
- * equally accented pulses. To recover a 2+2+3 or 3+2 felt-beat grouping, set
114
- * {@link TimeSignature.grouping}; {@link metricWeight} then accents each
115
- * group's head pulse.
116
- *
117
- * @param ts The time signature.
118
- * @returns The pulse count per bar.
119
- * @category Rhythm & Meter
120
- */
121
- declare function pulsesPerBar(ts: TimeSignature): number;
122
- /**
123
- * Convert an absolute quarter-note position to a bar index and in-bar offset.
124
- *
125
- * @param beatInQuarters Absolute position in quarter-note beats.
126
- * @param ts The time signature.
127
- * @returns The bar and in-bar quarter-note offset.
128
- * @example
129
- * ```ts
130
- * import { parseTimeSignature, beatToBarPosition } from '@libraz/libcantus';
131
- * const ts = parseTimeSignature('4/4');
132
- * beatToBarPosition(5, ts); // { bar: 1, beat: 1 }
133
- * ```
134
- * @category Rhythm & Meter
135
- */
136
- declare function beatToBarPosition(beatInQuarters: number, ts: TimeSignature): BarPosition;
137
- /**
138
- * Length of one felt beat (main pulse) in quarter-note beats.
139
- *
140
- * A quarter note in simple meters, a dotted quarter in compound ones, and one
141
- * denominator unit in an additive metre. Position displays, click tracks, and
142
- * score export are all written against this length.
143
- *
144
- * @param ts The time signature.
145
- * @returns The pulse length in quarter-note beats.
146
- * @example
147
- * ```ts
148
- * import { parseTimeSignature, pulseBeats } from '@libraz/libcantus';
149
- * pulseBeats(parseTimeSignature('6/8')); // 1.5
150
- * ```
151
- * @category Rhythm & Meter
152
- */
153
- declare function pulseBeats(ts: TimeSignature): number;
154
- /**
155
- * The felt-beat number of a bar position, 1-based.
156
- *
157
- * {@link BarPosition.beat} is a quarter-note offset, so in 6/8 the second felt
158
- * beat reads as 1.5. This is the number a musician says and a position display
159
- * shows.
160
- *
161
- * @param pos The bar position.
162
- * @param ts The time signature.
163
- * @returns The 1-based felt-beat number, fractional between pulses.
164
- * @example
165
- * ```ts
166
- * import { barPositionToPulse, parseTimeSignature } from '@libraz/libcantus';
167
- * barPositionToPulse({ bar: 0, beat: 1.5 }, parseTimeSignature('6/8')); // 2
168
- * ```
169
- * @category Rhythm & Meter
170
- */
171
- declare function barPositionToPulse(pos: BarPosition, ts: TimeSignature): number;
172
- /**
173
- * Render an absolute position as the `bar.beat` a DAW or a score shows:
174
- * 1-based bar, 1-based felt beat.
175
- *
176
- * @param beatInQuarters Absolute position in quarter-note beats.
177
- * @param ts The time signature.
178
- * @param decimals Digits of the fractional beat to keep.
179
- * @returns The formatted position, e.g. `'3.2'` for bar 3, felt beat 2.
180
- * @example
181
- * ```ts
182
- * import { formatBarPosition, parseTimeSignature } from '@libraz/libcantus';
183
- * formatBarPosition(7.5, parseTimeSignature('6/8')); // '3.2'
184
- * ```
185
- * @category Rhythm & Meter
186
- */
187
- declare function formatBarPosition(beatInQuarters: number, ts: TimeSignature, decimals?: number): string;
188
- /**
189
- * Convert a bar index and in-bar offset back to an absolute quarter-note
190
- * position.
191
- *
192
- * @param pos The bar position.
193
- * @param ts The time signature.
194
- * @returns The absolute position in quarter-note beats.
195
- * @category Rhythm & Meter
196
- */
197
- declare function barPositionToBeat(pos: BarPosition, ts: TimeSignature): number;
198
- /**
199
- * Metric weight of a position within its bar, on a 0–3 scale:
200
- * 3 the downbeat, 2 a secondary strong pulse, 1 any other main pulse, and 0 an
201
- * off-pulse subdivision.
202
- *
203
- * The secondary strong pulse is the bar's midpoint in even simple/compound
204
- * meters. For an additive/irregular meter with {@link TimeSignature.grouping}
205
- * set (e.g. `[2, 2, 3]` for 7/8), each group's head pulse other than the
206
- * downbeat is the secondary strong pulse instead; without a grouping every
207
- * non-downbeat main pulse of such a meter weighs 1 (flat, evenly divided
208
- * pulses).
209
- *
210
- * @param beatInQuarters Absolute or in-bar quarter-note position.
211
- * @param ts The time signature.
212
- * @returns The metric weight (0–3).
213
- * @throws If the time signature is malformed, including a grouping that is not
214
- * a positive-integer list summing to the pulse count or the numerator.
215
- * @example
216
- * ```ts
217
- * import { parseTimeSignature, metricWeight } from '@libraz/libcantus';
218
- * const ts = parseTimeSignature('4/4');
219
- * metricWeight(0, ts); // 3 (the downbeat)
220
- * ```
221
- * @category Rhythm & Meter
222
- */
223
- declare function metricWeight(beatInQuarters: number, ts: TimeSignature): number;
224
- /**
225
- * Whether a position is metrically accented (weight 2 or more — a downbeat or a
226
- * secondary strong pulse).
227
- *
228
- * @param beatInQuarters Absolute or in-bar quarter-note position.
229
- * @param ts The time signature.
230
- * @returns True on strong beats.
231
- * @category Rhythm & Meter
232
- */
233
- declare function isStrongBeat(beatInQuarters: number, ts: TimeSignature): boolean;
234
- /**
235
- * Subdivide a span into `count` equal tuplet durations (e.g. an eighth-note
236
- * triplet is `tuplet(1, 3)`).
237
- *
238
- * @param totalBeats Total span in quarter-note beats.
239
- * @param count Number of equal parts.
240
- * @returns `count` equal durations summing to `totalBeats`.
241
- * @throws If `count` is not a positive integer.
242
- * @category Rhythm & Meter
243
- */
244
- declare function tuplet(totalBeats: number, count: number): number[];
245
-
246
- export { type BarPosition as B, type TimeSignature as T, barPositionToPulse as a, barPositionToBeat as b, beatToBarPosition as c, beatsPerBar as d, formatTimeSignature as e, formatBarPosition as f, isStrongBeat as g, pulseBeats as h, isCompound as i, pulsesPerBar as j, metricWeight as m, parseTimeSignature as p, tuplet as t };
@@ -1,272 +0,0 @@
1
- /**
2
- * Pitch spelling: letter-name notes, enharmonic-aware conversion, and spelled
3
- * intervals.
4
- *
5
- * The rest of the library works in pitch classes (0..11), which cannot tell a
6
- * G# from an Ab or an augmented fourth from a diminished fifth. This module adds
7
- * an explicit spelling layer — a note is a diatonic letter plus a chromatic
8
- * alteration — so interval quality (P/M/m/A/d) and enharmonic identity are
9
- * preserved.
10
- */
11
- /**
12
- * A spelled note: a diatonic letter, a chromatic alteration, and an optional
13
- * octave.
14
- *
15
- * - `letter` is 0..6 for C..B.
16
- * - `alter` is the number of semitones of alteration: -2 double-flat, -1 flat,
17
- * 0 natural, +1 sharp, +2 double-sharp.
18
- * - `octave` follows scientific pitch notation (middle C = C4 = MIDI 60). When
19
- * absent, the note denotes a bare pitch class.
20
- *
21
- * @category Pitch & Intervals
22
- */
23
- type Note = {
24
- letter: number;
25
- alter: number;
26
- octave?: number;
27
- };
28
- /**
29
- * The quality label of a spelled interval: perfect, major, minor, or one or
30
- * more augmentations or diminutions.
31
- *
32
- * This names how an interval is *spelled*. It is a different notion from
33
- * {@link ConsonanceClass}, which classifies how an interval *sounds* against
34
- * another voice.
35
- *
36
- * @see {@link ConsonanceClass}
37
- * @category Pitch & Intervals
38
- */
39
- type IntervalQualityLabel = 'P' | 'M' | 'm' | `A${string}` | `d${string}`;
40
- /**
41
- * A spelled interval: a diatonic number, a quality label, and its semitone span.
42
- *
43
- * @category Pitch & Intervals
44
- */
45
- type SpelledInterval = {
46
- /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
47
- number: number;
48
- /** Quality label: 'P', 'M', 'm', or repeated 'A'/'d' for (multiply) aug/dim. */
49
- quality: IntervalQualityLabel;
50
- /** Signed semitone distance from the first note to the second. */
51
- semitones: number;
52
- };
53
- /**
54
- * Reduce a MIDI pitch, a bare pitch class, or any signed offset to a pitch
55
- * class in [0, 11].
56
- *
57
- * This is the library's single definition of pitch-class arithmetic: every
58
- * layer routes through it so a fractional pitch reduces the same way
59
- * everywhere. A non-integral input is rounded to the nearest semitone first, so
60
- * a microtonal 60.6 reads as C# rather than C.
61
- *
62
- * @param value A MIDI pitch, pitch class, or signed semitone offset.
63
- * @returns The pitch class in [0, 11].
64
- * @example
65
- * ```ts
66
- * import { pitchClassOf } from '@libraz/libcantus';
67
- * pitchClassOf(61); // 1
68
- * pitchClassOf(-1); // 11
69
- * pitchClassOf(60.6); // 1
70
- * ```
71
- * @category Pitch & Intervals
72
- */
73
- declare function pitchClassOf(value: number): number;
74
- /**
75
- * Reduce a letter number or letter offset to a diatonic letter in [0, 6]
76
- * (0 = C .. 6 = B).
77
- *
78
- * @param value A letter number or signed letter offset.
79
- * @returns The diatonic letter in [0, 6].
80
- * @category Pitch & Intervals
81
- */
82
- declare function diatonicLetterOf(value: number): number;
83
- /**
84
- * The pitch class of a diatonic letter with no accidental (C = 0, D = 2, ...).
85
- *
86
- * @param letter A letter number or signed letter offset; reduced to [0, 6].
87
- * @returns The natural pitch class of that letter.
88
- * @category Pitch & Intervals
89
- */
90
- declare function naturalPitchClassOf(letter: number): number;
91
- /**
92
- * Parse scientific pitch notation into a {@link Note}.
93
- *
94
- * Accepts a letter (case-insensitive) followed by any number of same-direction
95
- * accidentals and an optional octave, e.g. `"C"`, `"C#4"`, `"Bb"`, `"F##3"`,
96
- * `"Ebb2"`. Both `#` and `x` (double-sharp) are accepted for sharps; `b` for
97
- * flats. Mixing sharps and flats (e.g. `"C#b"`) is rejected.
98
- *
99
- * @param text The note text.
100
- * @returns The parsed note.
101
- * @throws If the text is not a valid note.
102
- * @example
103
- * ```ts
104
- * import { parseNote, noteToMidi } from '@libraz/libcantus';
105
- * const n = parseNote('C#4');
106
- * noteToMidi(n); // 61
107
- * ```
108
- * @category Pitch & Intervals
109
- */
110
- declare function parseNote(text: string): Note;
111
- /**
112
- * Render a {@link Note} as scientific pitch notation.
113
- *
114
- * @param note The note to format.
115
- * @returns The note text, including the octave when present.
116
- * @example
117
- * ```ts
118
- * import { formatNote, parseNote } from '@libraz/libcantus';
119
- * formatNote(parseNote('C#4')); // 'C#4'
120
- * ```
121
- * @category Pitch & Intervals
122
- */
123
- declare function formatNote(note: Note): string;
124
- /**
125
- * Pitch class (0..11) of a note, ignoring octave.
126
- *
127
- * @param note The note.
128
- * @returns The pitch class.
129
- * @example
130
- * ```ts
131
- * import { noteToPitchClass, parseNote } from '@libraz/libcantus';
132
- * noteToPitchClass(parseNote('Db')); // 1
133
- * ```
134
- * @category Pitch & Intervals
135
- */
136
- declare function noteToPitchClass(note: Note): number;
137
- /**
138
- * MIDI number of a note (middle C = C4 = 60).
139
- *
140
- * @param note The note; must carry an octave.
141
- * @returns The MIDI number.
142
- * @throws If the note has no octave.
143
- * @example
144
- * ```ts
145
- * import { noteToMidi, parseNote } from '@libraz/libcantus';
146
- * noteToMidi(parseNote('A4')); // 69
147
- * ```
148
- * @category Pitch & Intervals
149
- */
150
- declare function noteToMidi(note: Note): number;
151
- /**
152
- * Name a MIDI number as a {@link Note}, choosing sharp or flat spelling.
153
- *
154
- * The input is rounded to the nearest integer but is not clamped to the MIDI
155
- * range 0..127: out-of-range values extrapolate linearly (e.g. `-1` -> `B-2`,
156
- * `128` -> `G#9`) and remain an exact inverse of {@link noteToMidi}.
157
- *
158
- * @param midi The MIDI number.
159
- * @param spelling Whether to prefer sharps or flats for black keys.
160
- * @returns The spelled note, with octave.
161
- * @example
162
- * ```ts
163
- * import { midiToNote, formatNote } from '@libraz/libcantus';
164
- * formatNote(midiToNote(61)); // 'C#4'
165
- * formatNote(midiToNote(61, 'flat')); // 'Db4'
166
- * ```
167
- * @category Pitch & Intervals
168
- */
169
- declare function midiToNote(midi: number, spelling?: 'sharp' | 'flat'): Note;
170
- /**
171
- * Transpose a note by a signed number of semitones, keeping its spelling.
172
- *
173
- * The letter moves by the diatonic distance of the conventional interval for
174
- * that many semitones and the accidental is recomputed for the new letter, so
175
- * enharmonic identity survives: `Ab4` up a major second is `Bb4`, not `A#4`, and
176
- * the operation is an exact inverse of itself. An octave-less note stays
177
- * octave-less.
178
- *
179
- * A bare semitone count cannot distinguish an augmented fourth from a diminished
180
- * fifth; pass `spelling` to override the letter distance when the enharmonic
181
- * choice matters.
182
- *
183
- * @param note The note to transpose.
184
- * @param semitones The signed semitone offset.
185
- * @param opts `spelling` forces the result onto the sharp or flat side instead
186
- * of following the source note's letter.
187
- * @returns The transposed note.
188
- * @example
189
- * ```ts
190
- * import { transposeNote, formatNote, parseNote } from '@libraz/libcantus';
191
- * formatNote(transposeNote(parseNote('Ab4'), 2)); // 'Bb4'
192
- * formatNote(transposeNote(parseNote('Ab4'), 2, { spelling: 'sharp' })); // 'A#4'
193
- * ```
194
- * @category Pitch & Intervals
195
- */
196
- declare function transposeNote(note: Note, semitones: number, opts?: {
197
- spelling?: 'sharp' | 'flat';
198
- }): Note;
199
- /**
200
- * The semitone span of the interval named by a diatonic number and a quality.
201
- *
202
- * The inverse of the quality derivation in {@link spelledInterval}: it turns a
203
- * name back into a distance, which is what transposing by a named interval or
204
- * parsing one from text needs.
205
- *
206
- * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
207
- * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
208
- * @returns The unsigned semitone span.
209
- * @throws If the quality cannot apply to the number, such as a major fifth.
210
- * @example
211
- * ```ts
212
- * import { intervalSemitones } from '@libraz/libcantus';
213
- * intervalSemitones(5, 'P'); // 7
214
- * ```
215
- * @category Pitch & Intervals
216
- */
217
- declare function intervalSemitones(numberValue: number, quality: IntervalQualityLabel): number;
218
- /**
219
- * Parse an interval name such as `'P5'`, `'m3'`, or `'AA4'`.
220
- *
221
- * @param name The interval name: a quality label followed by a diatonic number.
222
- * @returns The spelled interval, with an ascending (positive) span.
223
- * @throws If the name is not a quality label followed by a number, or the two
224
- * cannot describe the same interval.
225
- * @example
226
- * ```ts
227
- * import { parseInterval } from '@libraz/libcantus';
228
- * parseInterval('m3'); // { number: 3, quality: 'm', semitones: 3 }
229
- * ```
230
- * @category Pitch & Intervals
231
- */
232
- declare function parseInterval(name: string): SpelledInterval;
233
- /**
234
- * Transpose a note by a spelled interval, keeping the spelling the interval
235
- * names.
236
- *
237
- * Unlike {@link transposeNote}, which picks a letter from the semitone count,
238
- * the diatonic number decides the letter here: transposing C by an augmented
239
- * second gives D#, not Eb.
240
- *
241
- * @param note The note to transpose.
242
- * @param interval The interval to apply; a negative span transposes downward.
243
- * @returns The transposed note, octave-less if the source was.
244
- * @example
245
- * ```ts
246
- * import { formatNote, parseInterval, parseNote, transposeByInterval } from '@libraz/libcantus';
247
- * formatNote(transposeByInterval(parseNote('C4'), parseInterval('A2'))); // 'D#4'
248
- * ```
249
- * @category Pitch & Intervals
250
- */
251
- declare function transposeByInterval(note: Note, interval: SpelledInterval): Note;
252
- /**
253
- * The spelled interval from one note to another.
254
- *
255
- * When both notes carry octaves, the interval reflects their full signed
256
- * distance (so `C4 -> C5` is a perfect octave). When either lacks an octave the
257
- * interval is measured within a single ascending octave from `a` to `b`.
258
- *
259
- * @param a The lower/first note.
260
- * @param b The upper/second note.
261
- * @returns The diatonic number, quality, and semitone span.
262
- * @example
263
- * ```ts
264
- * import { spelledInterval, parseNote } from '@libraz/libcantus';
265
- * spelledInterval(parseNote('C4'), parseNote('G4'));
266
- * // { number: 5, quality: 'P', semitones: 7 }
267
- * ```
268
- * @category Pitch & Intervals
269
- */
270
- declare function spelledInterval(a: Note, b: Note): SpelledInterval;
271
-
272
- export { type IntervalQualityLabel as I, type Note as N, type SpelledInterval as S, noteToMidi as a, noteToPitchClass as b, parseNote as c, diatonicLetterOf as d, pitchClassOf as e, formatNote as f, transposeNote as g, intervalSemitones as i, midiToNote as m, naturalPitchClassOf as n, parseInterval as p, spelledInterval as s, transposeByInterval as t };