@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,679 +0,0 @@
1
- import {
2
- HARMONIC_MINOR_MASK,
3
- MAJOR_MASK,
4
- MELODIC_MINOR_MASK,
5
- NATURAL_MINOR_MASK,
6
- chordPitchClasses,
7
- chordQualities,
8
- isScaleTone,
9
- majorKey,
10
- makeChord,
11
- scaleTonesInDegreeOrder
12
- } from "./chunk-D2FAJBYL.js";
13
- import {
14
- InvalidInputError,
15
- assertFiniteNumber,
16
- assertGenerationBudget,
17
- assertInteger,
18
- pitchClassOf
19
- } from "./chunk-XEBG3G53.js";
20
-
21
- // src/analyze/detect/index.ts
22
- function uniquePitchClasses(pitches) {
23
- return [...new Set(pitches.map(pitchClassOf))].sort((a, b) => a - b);
24
- }
25
- function assertPitches(pitches) {
26
- assertGenerationBudget(pitches.length, "detection pitches");
27
- for (let index = 0; index < pitches.length; index += 1) {
28
- assertFiniteNumber(pitches[index] ?? Number.NaN, `pitches[${index}]`);
29
- }
30
- }
31
- function detectChord(pitches, opts = {}) {
32
- assertPitches(pitches);
33
- if (opts.bassPc !== void 0) assertFiniteNumber(opts.bassPc, "bassPc");
34
- const input = uniquePitchClasses(pitches);
35
- if (input.length === 0) {
36
- return [];
37
- }
38
- const inputKind = opts.input === void 0 || opts.input === "auto" ? pitches.every((pitch) => Number.isInteger(pitch) && pitch >= 0 && pitch <= 11) ? "pitchClass" : "midi" : opts.input;
39
- const bassPc = opts.bassPc !== void 0 ? pitchClassOf(opts.bassPc) : inputKind === "midi" ? pitchClassOf(pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
40
- const inputSet = new Set(input);
41
- const matches = [];
42
- const qualities = chordQualities();
43
- for (const rootPc of input) {
44
- for (const quality of qualities) {
45
- const chord = makeChord(rootPc, quality);
46
- const tones = chordPitchClasses(chord);
47
- const toneSet = new Set(tones);
48
- const missingPcs = tones.filter((pc) => !inputSet.has(pc));
49
- const fifthPc = pitchClassOf(rootPc + 7);
50
- if (missingPcs.length > 1 || missingPcs.length === 1 && missingPcs[0] !== fifthPc) {
51
- continue;
52
- }
53
- const extraPcs = input.filter((pc) => !toneSet.has(pc));
54
- const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => pitchClassOf(rootPc + iv) === bassPc);
55
- const inversion = bassPc === void 0 || bassIndex < 0 ? null : bassIndex;
56
- const match = {
57
- rootPc,
58
- quality,
59
- missingPcs,
60
- extraPcs,
61
- exact: extraPcs.length === 0 && missingPcs.length === 0,
62
- inversion
63
- };
64
- if (bassPc !== void 0 && bassPc !== rootPc) {
65
- match.bassPc = bassPc;
66
- }
67
- matches.push(match);
68
- }
69
- }
70
- matches.sort((a, b) => {
71
- if (a.extraPcs.length !== b.extraPcs.length) {
72
- return a.extraPcs.length - b.extraPcs.length;
73
- }
74
- if (a.missingPcs.length !== b.missingPcs.length) {
75
- return a.missingPcs.length - b.missingPcs.length;
76
- }
77
- if (a.inversion === 0 !== (b.inversion === 0)) {
78
- return a.inversion === 0 ? -1 : 1;
79
- }
80
- const aSize = chordPitchClasses(makeChord(a.rootPc, a.quality)).length;
81
- const bSize = chordPitchClasses(makeChord(b.rootPc, b.quality)).length;
82
- return bSize - aSize;
83
- });
84
- return matches;
85
- }
86
- function detectChordBest(pitches, opts = {}) {
87
- const best = detectChord(pitches, opts)[0];
88
- if (!best) {
89
- return null;
90
- }
91
- return makeChord(best.rootPc, best.quality, best.bassPc);
92
- }
93
- var MINOR_VARIANTS = [
94
- { variant: "natural", mask: NATURAL_MINOR_MASK },
95
- { variant: "harmonic", mask: HARMONIC_MINOR_MASK },
96
- { variant: "melodic", mask: MELODIC_MINOR_MASK }
97
- ];
98
- var MAJOR_VARIANTS = [{ variant: "major", mask: MAJOR_MASK }];
99
- function detectKey(pitches, opts = {}) {
100
- assertPitches(pitches);
101
- const input = uniquePitchClasses(pitches);
102
- if (input.length === 0) {
103
- return [];
104
- }
105
- const weights = opts.weights;
106
- if (weights !== void 0 && weights.length !== pitches.length) {
107
- throw new InvalidInputError("weights must have one entry per pitch");
108
- }
109
- const counts = /* @__PURE__ */ new Map();
110
- let total = 0;
111
- for (let index = 0; index < pitches.length; index += 1) {
112
- const weight = weights === void 0 ? 1 : weights[index] ?? 0;
113
- assertFiniteNumber(weight, `weights[${index}]`);
114
- if (weight <= 0) {
115
- continue;
116
- }
117
- const pc = pitchClassOf(pitches[index] ?? 0);
118
- counts.set(pc, (counts.get(pc) ?? 0) + weight);
119
- total += weight;
120
- }
121
- if (total === 0) {
122
- return [];
123
- }
124
- const results = [];
125
- for (let tonic = 0; tonic < 12; tonic += 1) {
126
- for (const mode of ["major", "minor"]) {
127
- const variants = mode === "major" ? MAJOR_VARIANTS : MINOR_VARIANTS;
128
- let inScale = 0;
129
- let weighted = Number.NEGATIVE_INFINITY;
130
- let best = variants[0];
131
- for (const candidate of variants) {
132
- let variantInScale = 0;
133
- let variantWeighted = 0;
134
- for (const pc of input) {
135
- const offset = (pc - tonic + 12) % 12;
136
- if (candidate.mask >> offset & 1) {
137
- variantInScale += 1;
138
- }
139
- }
140
- for (const [pc, count] of counts) {
141
- const offset = (pc - tonic + 12) % 12;
142
- if (candidate.mask >> offset & 1) {
143
- variantWeighted += count;
144
- }
145
- }
146
- if (variantWeighted > weighted) {
147
- weighted = variantWeighted;
148
- inScale = variantInScale;
149
- best = candidate;
150
- }
151
- }
152
- weighted += (counts.get(tonic) ?? 0) * 0.5;
153
- results.push({
154
- key: { rootPc: tonic, modeMask12: best.mask },
155
- mode,
156
- variant: best.variant,
157
- fit: inScale / input.length,
158
- score: weighted / total
159
- });
160
- }
161
- }
162
- results.sort((a, b) => b.score - a.score);
163
- return results;
164
- }
165
- var DEFAULT_VELOCITY = 100;
166
- function detectKeyFromNotes(notes) {
167
- const sounding = notes.filter((note) => note.durationBeat > 0);
168
- return detectKey(
169
- sounding.map((note) => note.pitch),
170
- { weights: sounding.map((note) => note.durationBeat * (note.velocity ?? DEFAULT_VELOCITY)) }
171
- );
172
- }
173
- function detectKeyBest(pitches, opts = {}) {
174
- return detectKey(pitches, opts)[0] ?? null;
175
- }
176
-
177
- // src/analyze/functional/internal.ts
178
- function degreeRootPc(degreeNumber, key) {
179
- const tones = scaleTonesInDegreeOrder(key);
180
- if (tones.length === 0) {
181
- return pitchClassOf(key.rootPc);
182
- }
183
- const index = ((degreeNumber - 1) % tones.length + tones.length) % tones.length;
184
- return tones[index] ?? pitchClassOf(key.rootPc);
185
- }
186
- function loweredDegrees(key) {
187
- const tones = scaleTonesInDegreeOrder(key);
188
- const out = /* @__PURE__ */ new Set();
189
- if (tones.length !== 7) {
190
- return out;
191
- }
192
- const major = scaleTonesInDegreeOrder({ rootPc: pitchClassOf(key.rootPc), modeMask12: MAJOR_MASK });
193
- for (let index = 0; index < 7; index += 1) {
194
- const tone = tones[index];
195
- const majorTone = major[index];
196
- if (tone !== void 0 && majorTone !== void 0 && pitchClassOf(majorTone - tone) === 1) {
197
- out.add(index + 1);
198
- }
199
- }
200
- return out;
201
- }
202
- function romanReference(key) {
203
- return scaleTonesInDegreeOrder(key).length === 7 ? key : majorKey(pitchClassOf(key.rootPc));
204
- }
205
- function isNeapolitan(chord, key) {
206
- return pitchClassOf(chord.rootPc - key.rootPc) === 1 && chord.quality === "maj";
207
- }
208
-
209
- // src/analyze/functional/roman.ts
210
- var ROMAN = ["I", "II", "III", "IV", "V", "VI", "VII"];
211
- var ROMAN_TO_DEGREE = {
212
- I: 1,
213
- II: 2,
214
- III: 3,
215
- IV: 4,
216
- V: 5,
217
- VI: 6,
218
- VII: 7
219
- };
220
- var OFFSET_SPELLING = [
221
- [1, ""],
222
- [2, "b"],
223
- [2, ""],
224
- [3, "b"],
225
- [3, ""],
226
- [4, ""],
227
- [4, "#"],
228
- [5, ""],
229
- [6, "b"],
230
- [6, ""],
231
- [7, "b"],
232
- [7, ""]
233
- ];
234
- function seventhQuality(base, halfDim) {
235
- if (halfDim) {
236
- return "m7b5";
237
- }
238
- if (base === "dim") {
239
- return "dim7";
240
- }
241
- if (base === "aug") {
242
- return "aug7";
243
- }
244
- return base === "maj" ? "dom7" : "min7";
245
- }
246
- var INVERSION_FIGURES = /* @__PURE__ */ new Set(["6", "64", "65", "43", "42", "2", "7"]);
247
- var ROMAN_STYLE = {
248
- maj: { lower: false, suffix: "" },
249
- min: { lower: true, suffix: "" },
250
- dim: { lower: true, suffix: "o" },
251
- aug: { lower: false, suffix: "+" },
252
- maj7: { lower: false, suffix: "maj7" },
253
- min7: { lower: true, suffix: "7" },
254
- dom7: { lower: false, suffix: "7" },
255
- dim7: { lower: true, suffix: "o7" },
256
- m7b5: { lower: true, suffix: "\xF87" },
257
- minMaj7: { lower: true, suffix: "maj7" },
258
- aug7: { lower: false, suffix: "+7" },
259
- augMaj7: { lower: false, suffix: "+maj7" },
260
- majb5: { lower: false, suffix: "b5" },
261
- "6": { lower: false, suffix: "add6" },
262
- min6: { lower: true, suffix: "add6" },
263
- "6/9": { lower: false, suffix: "69" },
264
- sus2: { lower: false, suffix: "sus2" },
265
- sus4: { lower: false, suffix: "sus4" },
266
- add9: { lower: false, suffix: "add9" },
267
- add11: { lower: false, suffix: "add11" },
268
- maj9: { lower: false, suffix: "maj9" },
269
- min9: { lower: true, suffix: "9" },
270
- dom9: { lower: false, suffix: "9" },
271
- "7b9": { lower: false, suffix: "7b9" },
272
- "7#9": { lower: false, suffix: "7#9" },
273
- "7#11": { lower: false, suffix: "7#11" },
274
- "7b13": { lower: false, suffix: "7b13" },
275
- "11": { lower: false, suffix: "11" },
276
- "13": { lower: false, suffix: "13" },
277
- "5": { lower: false, suffix: "5" },
278
- "7sus4": { lower: false, suffix: "7sus4" },
279
- "7b5": { lower: false, suffix: "7b5" },
280
- "7alt": { lower: false, suffix: "7alt" },
281
- "13b9": { lower: false, suffix: "13b9" },
282
- maj13: { lower: false, suffix: "maj13" },
283
- "maj7#11": { lower: false, suffix: "maj7#11" },
284
- min11: { lower: true, suffix: "11" },
285
- min13: { lower: true, suffix: "13" },
286
- minAdd9: { lower: true, suffix: "add9" },
287
- "min6/9": { lower: true, suffix: "69" }
288
- };
289
- var SUFFIX_QUALITY = new Map(
290
- Object.entries(ROMAN_STYLE).map(
291
- ([quality, style]) => [`${style.lower ? "l" : "u"}:${style.suffix}`, quality]
292
- )
293
- );
294
- function parseInversion(figures) {
295
- if (figures === "65") {
296
- return { inversion: 1, seventh: true };
297
- }
298
- if (figures === "43") {
299
- return { inversion: 2, seventh: true };
300
- }
301
- if (figures === "42" || figures === "2") {
302
- return { inversion: 3, seventh: true };
303
- }
304
- if (figures === "7") {
305
- return { inversion: 0, seventh: true };
306
- }
307
- if (figures === "64") {
308
- return { inversion: 2, seventh: false };
309
- }
310
- if (figures === "6") {
311
- return { inversion: 1, seventh: false };
312
- }
313
- return { inversion: 0, seventh: false };
314
- }
315
- function parseSimpleRoman(text, key) {
316
- const match = /^([b#]?)([iIvV]+)(.*)$/.exec(text.trim());
317
- if (!match) {
318
- throw new InvalidInputError(`Invalid Roman numeral: ${text}`);
319
- }
320
- const accidental = match[1] === "b" ? -1 : match[1] === "#" ? 1 : 0;
321
- const glyph = (match[2] ?? "").toUpperCase();
322
- const degreeNumber = ROMAN_TO_DEGREE[glyph];
323
- if (degreeNumber === void 0) {
324
- throw new InvalidInputError(`Invalid Roman numeral: ${text}`);
325
- }
326
- const isUpper = (match[2] ?? "")[0] === (match[2] ?? "")[0]?.toUpperCase();
327
- const suffix = match[3] ?? "";
328
- const frame = romanReference(key);
329
- const redundantFlat = accidental === -1 && loweredDegrees(frame).has(degreeNumber);
330
- const rootPc = pitchClassOf(degreeRootPc(degreeNumber, frame) + (redundantFlat ? 0 : accidental));
331
- const canonical = SUFFIX_QUALITY.get(`${isUpper ? "u" : "l"}:${suffix}`);
332
- if (canonical !== void 0) {
333
- return { rootPc, quality: canonical, inversion: 0 };
334
- }
335
- const parsedSuffix = /^(o|°|dim|ø|\+|aug)?(maj7|M7)?(65|64|43|42|7|6|2)?$/.exec(suffix);
336
- if (!parsedSuffix) {
337
- throw new InvalidInputError(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
338
- }
339
- const qualityMarker = parsedSuffix[1] ?? "";
340
- const isDim = qualityMarker === "o" || qualityMarker === "\xB0" || qualityMarker === "dim";
341
- const isAug = qualityMarker === "+" || qualityMarker === "aug";
342
- const isHalfDim = qualityMarker === "\xF8";
343
- const explicitMaj7 = parsedSuffix[2] !== void 0;
344
- const figures = parsedSuffix[3] ?? "";
345
- if (figures !== "" && !INVERSION_FIGURES.has(figures)) {
346
- throw new InvalidInputError(
347
- `Unsupported figured-bass or extension "${figures}" in Roman numeral: ${text}`
348
- );
349
- }
350
- const { inversion, seventh } = parseInversion(figures);
351
- const hasSeventh = explicitMaj7 || seventh || isHalfDim;
352
- let base;
353
- if (isDim || isHalfDim) {
354
- base = "dim";
355
- } else if (isAug) {
356
- base = "aug";
357
- } else {
358
- base = isUpper ? "maj" : "min";
359
- }
360
- let quality;
361
- if (explicitMaj7) {
362
- quality = base === "min" ? "minMaj7" : base === "aug" ? "augMaj7" : "maj7";
363
- } else if (hasSeventh) {
364
- quality = seventhQuality(base, isHalfDim);
365
- } else if (base === "dim") {
366
- quality = "dim";
367
- } else if (base === "aug") {
368
- quality = "aug";
369
- } else {
370
- quality = base;
371
- }
372
- return { rootPc, quality, inversion };
373
- }
374
- function chordFromParsed(parsed) {
375
- const chord = makeChord(parsed.rootPc, parsed.quality);
376
- if (parsed.inversion > 0 && parsed.inversion < chord.intervals.length) {
377
- chord.bassPc = pitchClassOf(parsed.rootPc + (chord.intervals[parsed.inversion] ?? 0));
378
- }
379
- return chord;
380
- }
381
- function romanToChord(text, key) {
382
- const trimmed = text.trim();
383
- const slash = trimmed.indexOf("/");
384
- if (slash >= 0) {
385
- const applied = trimmed.slice(0, slash);
386
- const target = trimmed.slice(slash + 1);
387
- const targetRoot = parseSimpleRoman(target, key).rootPc;
388
- const localKey = majorKey(targetRoot);
389
- return chordFromParsed(parseSimpleRoman(applied, localKey));
390
- }
391
- return chordFromParsed(parseSimpleRoman(trimmed, key));
392
- }
393
- function romanStyle(quality) {
394
- return ROMAN_STYLE[quality];
395
- }
396
- function romanSpelling(rootPc, key) {
397
- const tones = scaleTonesInDegreeOrder(romanReference(key));
398
- const diatonic = tones.indexOf(pitchClassOf(rootPc));
399
- if (diatonic >= 0) {
400
- return { degreeNumber: diatonic + 1, accidental: "" };
401
- }
402
- const lowered = loweredDegrees(romanReference(key));
403
- for (let i = 0; i < tones.length; i += 1) {
404
- if (lowered.has(i + 1) && pitchClassOf((tones[i] ?? 0) + 1) === pitchClassOf(rootPc)) {
405
- return { degreeNumber: i + 1, accidental: "#" };
406
- }
407
- }
408
- for (let i = 0; i < tones.length; i += 1) {
409
- const degreeNumber2 = i + 1;
410
- if (degreeNumber2 === 1 || degreeNumber2 === 5) {
411
- continue;
412
- }
413
- if (pitchClassOf((tones[i] ?? 0) - 1) === pitchClassOf(rootPc)) {
414
- return { degreeNumber: degreeNumber2, accidental: "b" };
415
- }
416
- }
417
- for (let i = 0; i < tones.length; i += 1) {
418
- if (pitchClassOf((tones[i] ?? 0) + 1) === pitchClassOf(rootPc)) {
419
- return { degreeNumber: i + 1, accidental: "#" };
420
- }
421
- }
422
- const [degreeNumber, accidental] = OFFSET_SPELLING[pitchClassOf(rootPc - key.rootPc)] ?? [1, ""];
423
- return { degreeNumber, accidental };
424
- }
425
- function baseMarker(quality) {
426
- if (quality === "dim" || quality === "dim7") {
427
- return "o";
428
- }
429
- if (quality === "m7b5") {
430
- return "\xF8";
431
- }
432
- if (quality === "aug" || quality === "aug7") {
433
- return "+";
434
- }
435
- if (quality === "augMaj7") {
436
- return "+maj7";
437
- }
438
- if (quality === "maj7" || quality === "minMaj7") {
439
- return "maj7";
440
- }
441
- return "";
442
- }
443
- var TRIAD_FIGURES = { 1: "6", 2: "64" };
444
- var SEVENTH_FIGURES = { 1: "65", 2: "43", 3: "42" };
445
- var TRIAD_FIGURE_QUALITIES = /* @__PURE__ */ new Set(["maj", "min", "dim", "aug"]);
446
- var SEVENTH_FIGURE_QUALITIES = /* @__PURE__ */ new Set([
447
- "maj7",
448
- "min7",
449
- "dom7",
450
- "dim7",
451
- "m7b5",
452
- "minMaj7",
453
- "aug7",
454
- "augMaj7"
455
- ]);
456
- function numeralFor(degreeNumber, lower) {
457
- const glyph = ROMAN[degreeNumber - 1];
458
- if (glyph === void 0) {
459
- throw new InvalidInputError(`no Roman numeral for scale degree ${degreeNumber}`);
460
- }
461
- return lower ? glyph.toLowerCase() : glyph;
462
- }
463
- function isDiatonicChord(chord, key) {
464
- return chordPitchClasses(chord).every((pc) => isScaleTone(pc, key));
465
- }
466
- function isAppliedDominantSonority(chord) {
467
- const has = (semitones) => chord.intervals.some((i) => pitchClassOf(i) === semitones);
468
- return chord.quality === "maj" || has(4) && has(10);
469
- }
470
- var LEADING_TONE_QUALITIES = /* @__PURE__ */ new Set(["dim", "dim7", "m7b5"]);
471
- function degreeTriad(tones, index) {
472
- const root = tones[index] ?? 0;
473
- return {
474
- third: pitchClassOf((tones[(index + 2) % tones.length] ?? 0) - root),
475
- fifth: pitchClassOf((tones[(index + 4) % tones.length] ?? 0) - root)
476
- };
477
- }
478
- function appliedTarget(chord, key) {
479
- const dominant = isAppliedDominantSonority(chord);
480
- const leadingTone = LEADING_TONE_QUALITIES.has(chord.quality);
481
- if (!dominant && !leadingTone) {
482
- return null;
483
- }
484
- const tones = scaleTonesInDegreeOrder(romanReference(key));
485
- for (let index = 1; index < tones.length; index += 1) {
486
- const targetRoot = tones[index];
487
- if (targetRoot === void 0) {
488
- continue;
489
- }
490
- const triad = degreeTriad(tones, index);
491
- if (triad.fifth === 6) {
492
- continue;
493
- }
494
- const expectedRoot = pitchClassOf(targetRoot + (dominant ? 7 : -1));
495
- if (expectedRoot === pitchClassOf(chord.rootPc)) {
496
- return { degreeNumber: index + 1, rootPc: targetRoot, lower: triad.third === 3 };
497
- }
498
- }
499
- return null;
500
- }
501
- function chordToRoman(chord, key, opts = {}) {
502
- if (opts.applied === true && !isDiatonicChord(chord, key)) {
503
- const target = appliedTarget(chord, key);
504
- if (target !== null) {
505
- const local = chordToRoman(chord, majorKey(target.rootPc));
506
- return `${local}/${numeralFor(target.degreeNumber, target.lower)}`;
507
- }
508
- }
509
- const { degreeNumber, accidental } = romanSpelling(chord.rootPc, key);
510
- const { lower, suffix } = romanStyle(chord.quality);
511
- const cased = numeralFor(degreeNumber, lower);
512
- let inversion = 0;
513
- if (chord.bassPc !== void 0) {
514
- const idx = chord.intervals.findIndex((iv) => pitchClassOf(chord.rootPc + iv) === chord.bassPc);
515
- if (idx > 0) {
516
- inversion = idx;
517
- }
518
- }
519
- if (inversion > 0) {
520
- const figure = SEVENTH_FIGURE_QUALITIES.has(chord.quality) ? SEVENTH_FIGURES[inversion] : TRIAD_FIGURE_QUALITIES.has(chord.quality) ? TRIAD_FIGURES[inversion] : void 0;
521
- if (figure !== void 0) {
522
- return `${accidental}${cased}${baseMarker(chord.quality)}${figure}`;
523
- }
524
- }
525
- return `${accidental}${cased}${suffix}`;
526
- }
527
-
528
- // src/analyze/functional/borrowed.ts
529
- function isHarmonicMinorAlteration(chord, key) {
530
- return isMinorKey(key) && isDiatonic(chord, { rootPc: pitchClassOf(key.rootPc), modeMask12: HARMONIC_MINOR_MASK });
531
- }
532
- function isBorrowedChord(chord, key) {
533
- if (isDiatonic(chord, key)) {
534
- return false;
535
- }
536
- if (isHarmonicMinorAlteration(chord, key)) {
537
- return false;
538
- }
539
- return isDiatonic(chord, parallelKey(key));
540
- }
541
- function borrowedSource(chord, key) {
542
- if (isDiatonic(chord, key)) {
543
- return null;
544
- }
545
- if (isNeapolitan(chord, key)) {
546
- return "neapolitan";
547
- }
548
- if (isBorrowedChord(chord, key)) {
549
- return isMinorKey(key) ? "parallelMajor" : "parallelMinor";
550
- }
551
- return null;
552
- }
553
-
554
- // src/analyze/functional/function.ts
555
- var FUNCTION_BY_OFFSET = [
556
- "tonic",
557
- // 0 I
558
- "subdominant",
559
- // 1 bII (Neapolitan)
560
- "subdominant",
561
- // 2 ii
562
- "tonic",
563
- // 3 bIII
564
- "tonic",
565
- // 4 iii
566
- "subdominant",
567
- // 5 IV
568
- "dominant",
569
- // 6 #IV / bV
570
- "dominant",
571
- // 7 V
572
- "subdominant",
573
- // 8 bVI
574
- "tonic",
575
- // 9 vi
576
- "subdominant",
577
- // 10 bVII
578
- "dominant"
579
- // 11 vii
580
- ];
581
- function isMinorKey(key) {
582
- const hasMinorThird = key.modeMask12 >> 3 & 1;
583
- const hasMajorThird2 = key.modeMask12 >> 4 & 1;
584
- return Boolean(hasMinorThird) && !hasMajorThird2;
585
- }
586
- function functionOf(chord, key) {
587
- const offset = pitchClassOf(chord.rootPc - key.rootPc);
588
- if (isNeapolitan(chord, key)) {
589
- return "subdominant";
590
- }
591
- if (isAppliedDominant(chord, key)) {
592
- return "dominant";
593
- }
594
- if (!isMinorKey(key) && hasMajorThird(chord) && (offset === 8 || offset === 10)) {
595
- return "subdominant";
596
- }
597
- return FUNCTION_BY_OFFSET[offset] ?? "tonic";
598
- }
599
- function isAppliedDominant(chord, key) {
600
- if (isDiatonic(chord, key)) {
601
- return false;
602
- }
603
- const root = pitchClassOf(chord.rootPc);
604
- const resolvesTo = (step) => isScaleTone(pitchClassOf(root + step), key);
605
- if (hasMajorThird(chord) && chord.intervals.some((i) => pitchClassOf(i) === 10)) {
606
- return resolvesTo(5) || resolvesTo(11);
607
- }
608
- return isDiminishedQuality(chord.quality) && resolvesTo(1);
609
- }
610
- function isDiatonic(chord, key) {
611
- return chordPitchClasses(chord).every((pc) => isScaleTone(pc, key));
612
- }
613
- function parallelKey(key) {
614
- return {
615
- rootPc: pitchClassOf(key.rootPc),
616
- modeMask12: isMinorKey(key) ? MAJOR_MASK : NATURAL_MINOR_MASK
617
- };
618
- }
619
- function isDiminishedQuality(quality) {
620
- return quality === "dim" || quality === "dim7" || quality === "m7b5";
621
- }
622
- function hasMajorThird(chord) {
623
- return chord.intervals.some((interval) => pitchClassOf(interval) === 4);
624
- }
625
- function analyzeChord(chord, key) {
626
- const source = borrowedSource(chord, key);
627
- return {
628
- function: functionOf(chord, key),
629
- borrowed: source !== null,
630
- source,
631
- roman: chordToRoman(chord, key)
632
- };
633
- }
634
- function secondaryDominant(targetDegree, key) {
635
- assertInteger(targetDegree, "targetDegree", 0, 6);
636
- const targetRoot = degreeRootPc(targetDegree + 1, key);
637
- return makeChord(pitchClassOf(targetRoot + 7), "dom7");
638
- }
639
-
640
- // src/analyze/functional/cadence.ts
641
- function detectCadence(from, to, key) {
642
- const tonic = pitchClassOf(key.rootPc);
643
- const fromOffset = pitchClassOf(from.rootPc - tonic);
644
- const toOffset = pitchClassOf(to.rootPc - tonic);
645
- const deceptiveTargets = isMinorKey(key) ? [8] : [8, 9];
646
- if (fromOffset === 7 && toOffset === 0) {
647
- return "authentic";
648
- }
649
- if (fromOffset === 5 && toOffset === 0) {
650
- return "plagal";
651
- }
652
- if (fromOffset === 7 && deceptiveTargets.includes(toOffset)) {
653
- return "deceptive";
654
- }
655
- if (toOffset === 7 && fromOffset !== 7) {
656
- return "half";
657
- }
658
- return null;
659
- }
660
-
661
- export {
662
- detectChord,
663
- detectChordBest,
664
- detectKey,
665
- detectKeyFromNotes,
666
- detectKeyBest,
667
- romanToChord,
668
- chordToRoman,
669
- isMinorKey,
670
- functionOf,
671
- isDiatonic,
672
- parallelKey,
673
- analyzeChord,
674
- secondaryDominant,
675
- isBorrowedChord,
676
- borrowedSource,
677
- detectCadence
678
- };
679
- //# sourceMappingURL=chunk-KMHOICIY.js.map