@libraz/libcantus 1.2.0 → 1.4.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 (131) hide show
  1. package/NOTICE +63 -0
  2. package/README.md +7 -1
  3. package/README_ja.md +17 -11
  4. package/dist/analyze/index.cjs +31 -7
  5. package/dist/analyze/index.cjs.map +1 -1
  6. package/dist/analyze/index.d.cts +161 -13
  7. package/dist/analyze/index.d.ts +161 -13
  8. package/dist/analyze/index.js +37 -13
  9. package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
  10. package/dist/chunk-2G5MCGYU.js.map +1 -0
  11. package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
  12. package/dist/chunk-3FDBMIPR.cjs.map +1 -0
  13. package/dist/{chunk-HPVSH7SF.cjs → chunk-3MDOEFIQ.cjs} +618 -954
  14. package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
  15. package/dist/{chunk-FBH75DAY.js → chunk-5MWRBYAT.js} +560 -159
  16. package/dist/chunk-5MWRBYAT.js.map +1 -0
  17. package/dist/{chunk-J7FHVZII.cjs → chunk-6TTBEON3.cjs} +3074 -740
  18. package/dist/chunk-6TTBEON3.cjs.map +1 -0
  19. package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
  20. package/dist/chunk-7NQ6AL3H.js.map +1 -0
  21. package/dist/{chunk-HWHGOVO6.cjs → chunk-E3QO3UET.cjs} +31 -23
  22. package/dist/chunk-E3QO3UET.cjs.map +1 -0
  23. package/dist/{chunk-H2KJHBW4.js → chunk-FJFULJZE.js} +2375 -191
  24. package/dist/chunk-FJFULJZE.js.map +1 -0
  25. package/dist/{chunk-Q6LFSAKV.js → chunk-FK3DGAIZ.js} +2657 -323
  26. package/dist/chunk-FK3DGAIZ.js.map +1 -0
  27. package/dist/chunk-GEDTJHAC.cjs.map +1 -1
  28. package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
  29. package/dist/chunk-H7FVMBUC.js.map +1 -0
  30. package/dist/{chunk-DSMJRXCZ.cjs → chunk-HYSVSFNI.cjs} +931 -530
  31. package/dist/chunk-HYSVSFNI.cjs.map +1 -0
  32. package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
  33. package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
  34. package/dist/chunk-LQZQDBCD.cjs +5513 -0
  35. package/dist/chunk-LQZQDBCD.cjs.map +1 -0
  36. package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
  37. package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
  38. package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
  39. package/dist/chunk-S3QELJUE.cjs.map +1 -0
  40. package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
  41. package/dist/chunk-TXFWSU5D.js.map +1 -0
  42. package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
  43. package/dist/chunk-U4EQTDGN.cjs.map +1 -0
  44. package/dist/chunk-UGLFDG7L.cjs.map +1 -1
  45. package/dist/{chunk-HYJJ2V2I.js → chunk-UTE5A36R.js} +483 -819
  46. package/dist/chunk-UTE5A36R.js.map +1 -0
  47. package/dist/{chunk-NFJ4P6ZZ.js → chunk-UZKQISRF.js} +17 -9
  48. package/dist/chunk-UZKQISRF.js.map +1 -0
  49. package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
  50. package/dist/chunk-VL5NXVCN.js.map +1 -0
  51. package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
  52. package/dist/chunk-WOMCXN2W.cjs.map +1 -0
  53. package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
  54. package/dist/chunk-YDGMWH43.cjs.map +1 -0
  55. package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
  56. package/dist/chunk-YYVXRV3V.js.map +1 -0
  57. package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
  58. package/dist/chunk-ZLNSH4UL.js.map +1 -0
  59. package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
  60. package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
  61. package/dist/core/index.cjs +5 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +9 -8
  64. package/dist/core/index.d.ts +9 -8
  65. package/dist/core/index.js +4 -4
  66. package/dist/generate/index.cjs +20 -8
  67. package/dist/generate/index.cjs.map +1 -1
  68. package/dist/generate/index.d.cts +105 -17
  69. package/dist/generate/index.d.ts +105 -17
  70. package/dist/generate/index.js +19 -7
  71. package/dist/{index-CL4b4gUQ.d.ts → index-5VJgKz_Q.d.cts} +6 -2
  72. package/dist/{index-B9QLziDe.d.cts → index-BLuDesVG.d.ts} +20 -13
  73. package/dist/{index-BHIUyjhZ.d.ts → index-BV6APVa6.d.cts} +20 -13
  74. package/dist/{index-BMweUlm5.d.cts → index-Bf0Sv8a9.d.ts} +6 -2
  75. package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
  76. package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
  77. package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
  78. package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
  79. package/dist/{index-BNSM-DSk.d.cts → index-DVInzCDS.d.cts} +533 -28
  80. package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
  81. package/dist/{index-BJgP97N1.d.ts → index-jgeug9EA.d.ts} +533 -28
  82. package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
  83. package/dist/index.cjs +49 -13
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +13 -13
  86. package/dist/index.d.ts +13 -13
  87. package/dist/index.js +55 -19
  88. package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
  89. package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
  90. package/dist/model/index.cjs +12 -12
  91. package/dist/model/index.cjs.map +1 -1
  92. package/dist/model/index.d.cts +259 -48
  93. package/dist/model/index.d.ts +259 -48
  94. package/dist/model/index.js +11 -11
  95. package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
  96. package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
  97. package/dist/{index-BRMbPkHE.d.ts → profile-B4WqcM4H.d.ts} +85 -610
  98. package/dist/{index-DGCNKAUr.d.cts → profile-BJd1XkPB.d.cts} +85 -610
  99. package/dist/theory/index.cjs +5 -5
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +18 -14
  102. package/dist/theory/index.d.ts +18 -14
  103. package/dist/theory/index.js +4 -4
  104. package/dist/{index-CJBBiAmU.d.ts → types-7mGFwvMm.d.ts} +1210 -31
  105. package/dist/{index-BBWNZS0f.d.cts → types-_ofpb2be.d.cts} +1210 -31
  106. package/package.json +13 -13
  107. package/dist/chunk-35NBV2FT.cjs.map +0 -1
  108. package/dist/chunk-3R3QVKY5.cjs.map +0 -1
  109. package/dist/chunk-76ZBTRG2.cjs.map +0 -1
  110. package/dist/chunk-7KG3RTYS.js.map +0 -1
  111. package/dist/chunk-AGOVW7U6.js.map +0 -1
  112. package/dist/chunk-CGBADIO5.cjs.map +0 -1
  113. package/dist/chunk-DSMJRXCZ.cjs.map +0 -1
  114. package/dist/chunk-FBH75DAY.js.map +0 -1
  115. package/dist/chunk-H2KJHBW4.js.map +0 -1
  116. package/dist/chunk-HPVSH7SF.cjs.map +0 -1
  117. package/dist/chunk-HWHGOVO6.cjs.map +0 -1
  118. package/dist/chunk-HYJJ2V2I.js.map +0 -1
  119. package/dist/chunk-J7FHVZII.cjs.map +0 -1
  120. package/dist/chunk-M5RYMAFI.js.map +0 -1
  121. package/dist/chunk-NB25P3EH.js.map +0 -1
  122. package/dist/chunk-NFJ4P6ZZ.js.map +0 -1
  123. package/dist/chunk-NM7UAMC3.cjs.map +0 -1
  124. package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
  125. package/dist/chunk-PUKJP2Z5.js.map +0 -1
  126. package/dist/chunk-Q6LFSAKV.js.map +0 -1
  127. package/dist/chunk-RFIBF2RR.js.map +0 -1
  128. package/dist/chunk-S5BKZZGM.cjs.map +0 -1
  129. package/dist/chunk-SGLEQVJH.cjs +0 -3329
  130. package/dist/chunk-SGLEQVJH.cjs.map +0 -1
  131. package/dist/chunk-UMWZ4ZET.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/libcantus/libcantus/dist/chunk-GEDTJHAC.cjs"],"names":[],"mappings":"AAAA","file":"/home/runner/work/libcantus/libcantus/dist/chunk-GEDTJHAC.cjs"}
1
+ {"version":3,"sources":["/home/runner/work/libcantus/libcantus/ts/dist/chunk-GEDTJHAC.cjs"],"names":[],"mappings":"AAAA","file":"/home/runner/work/libcantus/libcantus/ts/dist/chunk-GEDTJHAC.cjs"}
@@ -33,7 +33,7 @@ import {
33
33
  tryParseKeyName,
34
34
  tryParseNote,
35
35
  unwrapParse
36
- } from "./chunk-AGOVW7U6.js";
36
+ } from "./chunk-YYVXRV3V.js";
37
37
 
38
38
  // src/theory/scale/masks.ts
39
39
  function maskFromOffsets(offsets) {
@@ -328,7 +328,9 @@ function toKeyScale(value) {
328
328
  return normalizedKeyScale(data);
329
329
  }
330
330
  }
331
- throw new InvalidInputError(`key must be a key name or a key/scale; received ${typeof value}`);
331
+ throw new InvalidInputError(
332
+ `key must be a key name or a key/scale; received ${describeRejected(value)}`
333
+ );
332
334
  }
333
335
 
334
336
  // src/theory/scale/degrees.ts
@@ -449,7 +451,7 @@ function rotateMask(mask, semitones) {
449
451
  const steps = (semitones % 12 + 12) % 12;
450
452
  return (mask >> steps | mask << 12 - steps) & 4095;
451
453
  }
452
- var DIATONIC_MODE_OFFSETS = /* @__PURE__ */ new Map([
454
+ var DIATONIC_MODE_OFFSET_ENTRIES = [
453
455
  [rotateMask(MAJOR_MASK, 5), 1],
454
456
  // lydian
455
457
  [MAJOR_MASK, 0],
@@ -464,7 +466,8 @@ var DIATONIC_MODE_OFFSETS = /* @__PURE__ */ new Map([
464
466
  // phrygian
465
467
  [rotateMask(MAJOR_MASK, 11), -5]
466
468
  // locrian
467
- ]);
469
+ ];
470
+ var DIATONIC_MODE_OFFSETS = new Map(DIATONIC_MODE_OFFSET_ENTRIES);
468
471
  function modeOffset(key) {
469
472
  const diatonic = DIATONIC_MODE_OFFSETS.get(key.modeMask12);
470
473
  if (diatonic !== void 0) {
@@ -1097,7 +1100,7 @@ function chordSpecQuality(spec) {
1097
1100
  }
1098
1101
  return nearest ?? BASE_QUALITY[checked.base];
1099
1102
  }
1100
- var EXTENSION_BY_SEMITONE = /* @__PURE__ */ new Map([
1103
+ var EXTENSION_ENTRIES = [
1101
1104
  [13, { degree: 9, alter: -1 }],
1102
1105
  [14, { degree: 9, alter: 0 }],
1103
1106
  [15, { degree: 9, alter: 1 }],
@@ -1107,7 +1110,8 @@ var EXTENSION_BY_SEMITONE = /* @__PURE__ */ new Map([
1107
1110
  [20, { degree: 13, alter: -1 }],
1108
1111
  [21, { degree: 13, alter: 0 }],
1109
1112
  [22, { degree: 13, alter: 1 }]
1110
- ]);
1113
+ ];
1114
+ var EXTENSION_BY_SEMITONE = new Map(EXTENSION_ENTRIES);
1111
1115
  function sameOffsets(a, b) {
1112
1116
  return a.length === b.length && a.every((offset, index) => offset === b[index]);
1113
1117
  }
@@ -1172,6 +1176,9 @@ function chordSpecFromIntervals(intervals) {
1172
1176
  const normalized = normalizeChordSpec(spec);
1173
1177
  return sameOffsets(specIntervals(normalized), offsets) ? normalized : void 0;
1174
1178
  }
1179
+ var ALTERABLE_DEGREES = [5, 9, 11, 13];
1180
+ var ALTERATION_STEPS = [-1, 0, 1];
1181
+ var ADDABLE_DEGREES = [6, 9, 11, 13];
1175
1182
  function assertPart(value, allowed, name) {
1176
1183
  if (!allowed.includes(value)) {
1177
1184
  throw new InvalidInputError(
@@ -1190,7 +1197,7 @@ function assertDegreeIn(value, allowed, name) {
1190
1197
  }
1191
1198
  function assertDegreeList(value, allowed, name) {
1192
1199
  if (!Array.isArray(value)) {
1193
- throw new InvalidInputError(`${name} must be an array; received ${typeof value}`);
1200
+ throw new InvalidInputError(`${name} must be an array; received ${describeRejected(value)}`);
1194
1201
  }
1195
1202
  for (const degree of value) {
1196
1203
  assertDegreeIn(degree, allowed, name);
@@ -1198,7 +1205,7 @@ function assertDegreeList(value, allowed, name) {
1198
1205
  }
1199
1206
  function assertChordSpec(spec) {
1200
1207
  if (spec === null || typeof spec !== "object") {
1201
- throw new InvalidInputError(`chord spec must be an object; received ${typeof spec}`);
1208
+ throw new InvalidInputError(`chord spec must be an object; received ${describeRejected(spec)}`);
1202
1209
  }
1203
1210
  assertFiniteNumber(spec.rootPc, "chord spec rootPc");
1204
1211
  if (spec.bassPc !== void 0) {
@@ -1216,10 +1223,10 @@ function assertChordSpec(spec) {
1216
1223
  throw new InvalidInputError("chord spec alterations must be an array");
1217
1224
  }
1218
1225
  for (const alteration of spec.alterations) {
1219
- assertDegreeIn(alteration?.degree, [5, 9, 11, 13], "chord spec alteration degree");
1220
- assertDegreeIn(alteration.alter, [-1, 0, 1], "chord spec alteration");
1226
+ assertDegreeIn(alteration?.degree, ALTERABLE_DEGREES, "chord spec alteration degree");
1227
+ assertDegreeIn(alteration.alter, ALTERATION_STEPS, "chord spec alteration");
1221
1228
  }
1222
- assertDegreeList(spec.additions, [6, 9, 11, 13], "chord spec additions");
1229
+ assertDegreeList(spec.additions, ADDABLE_DEGREES, "chord spec additions");
1223
1230
  assertDegreeList(spec.omissions, OMITTABLE_DEGREES, "chord spec omissions");
1224
1231
  return normalizeChordSpec(spec);
1225
1232
  }
@@ -1230,7 +1237,7 @@ function chordQualities() {
1230
1237
  return [...QUALITY_ORDER];
1231
1238
  }
1232
1239
  function assertChordQuality(quality) {
1233
- if (!Object.hasOwn(QUALITY_INTERVALS, quality)) {
1240
+ if (typeof quality !== "string" || !Object.hasOwn(QUALITY_INTERVALS, quality)) {
1234
1241
  throw new InvalidInputError(`Unknown chord quality: ${String(quality)}`);
1235
1242
  }
1236
1243
  return quality;
@@ -1244,17 +1251,20 @@ function assertChord(chord, name = "chord") {
1244
1251
  function chordFromDegree(degree, ext, key) {
1245
1252
  assertDegree(degree, "chord degree");
1246
1253
  assertChordQuality(ext);
1247
- const scale = toKeyScale(key);
1248
- const tones = scaleTonesInDegreeOrder(scale);
1254
+ const resolved = resolveKey(key);
1255
+ const tones = scaleTonesInDegreeOrder(resolved.scale);
1249
1256
  const length = tones.length;
1250
1257
  const index = length > 0 ? (degree - 1) % length : 0;
1251
- const rootPc = tones[index] ?? pitchClassOf(scale.rootPc);
1252
- return { rootPc, quality: ext, intervals: [...QUALITY_INTERVALS[ext]] };
1258
+ const rootPc = tones[index] ?? pitchClassOf(resolved.scale.rootPc);
1259
+ return makeSpelledChord(rootPc, ext, { rootSpelling: keySpelling(rootPc, resolved) });
1253
1260
  }
1254
1261
  function makeChord(rootPc, quality, bassPc) {
1262
+ return makeSpelledChord(rootPc, quality, { bassPc });
1263
+ }
1264
+ function makeSpelledChord(rootPc, quality, parts2 = {}) {
1255
1265
  assertFiniteNumber(rootPc, "chord rootPc");
1256
- if (bassPc !== void 0) {
1257
- assertFiniteNumber(bassPc, "chord bassPc");
1266
+ if (parts2.bassPc !== void 0) {
1267
+ assertFiniteNumber(parts2.bassPc, "chord bassPc");
1258
1268
  }
1259
1269
  assertChordQuality(quality);
1260
1270
  const chord = {
@@ -1262,11 +1272,32 @@ function makeChord(rootPc, quality, bassPc) {
1262
1272
  quality,
1263
1273
  intervals: [...QUALITY_INTERVALS[quality] ?? []]
1264
1274
  };
1265
- if (bassPc !== void 0) {
1266
- chord.bassPc = pitchClassOf(bassPc);
1275
+ if (parts2.bassPc !== void 0) {
1276
+ chord.bassPc = pitchClassOf(parts2.bassPc);
1277
+ }
1278
+ if (parts2.rootSpelling !== void 0) {
1279
+ chord.rootSpelling = { letter: parts2.rootSpelling.letter, alter: parts2.rootSpelling.alter };
1280
+ }
1281
+ if (parts2.bassSpelling !== void 0) {
1282
+ chord.bassSpelling = { letter: parts2.bassSpelling.letter, alter: parts2.bassSpelling.alter };
1283
+ } else if (chord.rootSpelling !== void 0 && chord.bassPc !== void 0) {
1284
+ const index = chord.intervals.findIndex(
1285
+ (interval) => pitchClassOf(chord.rootPc + interval) === chord.bassPc
1286
+ );
1287
+ const tone = index >= 0 ? spellChordFromRoot(chord, chord.rootSpelling)[index] : void 0;
1288
+ if (tone !== void 0) {
1289
+ chord.bassSpelling = { letter: tone.letter, alter: tone.alter };
1290
+ }
1291
+ }
1292
+ if (parts2.toneSpellings !== void 0) {
1293
+ chord.toneSpellings = parts2.toneSpellings.map(({ letter, alter }) => ({ letter, alter }));
1267
1294
  }
1268
1295
  return chord;
1269
1296
  }
1297
+ function keySpelling(pc, key) {
1298
+ const { letter, alter } = spellPitchClass(pc, key.tonic, key.scale);
1299
+ return { letter, alter };
1300
+ }
1270
1301
  function chordFromSpec(spec) {
1271
1302
  const checked = assertChordSpec(spec);
1272
1303
  const chord = {
@@ -1298,7 +1329,9 @@ function chordSpecOf(chord) {
1298
1329
  function copySpanIntervals(intervals) {
1299
1330
  const name = "chord span intervals";
1300
1331
  if (!Array.isArray(intervals)) {
1301
- throw new InvalidInputError(`${name} must be an array; received ${typeof intervals}`);
1332
+ throw new InvalidInputError(
1333
+ `${name} must be an array; received ${describeRejected(intervals)}`
1334
+ );
1302
1335
  }
1303
1336
  if (intervals.length === 0) {
1304
1337
  throw new InvalidInputError(`${name} must name at least one interval`);
@@ -1322,7 +1355,7 @@ function hasCustomIntervals(chord) {
1322
1355
  }
1323
1356
  function chordFromSpan(span) {
1324
1357
  assertRecord(span, "chord span");
1325
- const chord = makeChord(span.rootPc, span.quality, span.bassPc);
1358
+ const chord = makeSpelledChord(span.rootPc, span.quality, { bassPc: span.bassPc });
1326
1359
  if (span.intervals !== void 0) {
1327
1360
  chord.intervals = copySpanIntervals(span.intervals);
1328
1361
  }
@@ -1558,8 +1591,9 @@ function matchesQualityIntervals(quality, intervals) {
1558
1591
  const template = QUALITY_INTERVALS[quality];
1559
1592
  return template.length === intervals.length && template.every((interval, index) => interval === intervals[index]);
1560
1593
  }
1561
- function stackThirds(degree, key, size) {
1594
+ function stackThirds(degree, resolved, size) {
1562
1595
  assertDegree(degree, "chord degree");
1596
+ const key = resolved.scale;
1563
1597
  const tones = scaleTonesInDegreeOrder(key);
1564
1598
  const length = tones.length;
1565
1599
  if (length !== 7) {
@@ -1581,13 +1615,13 @@ function stackThirds(degree, key, size) {
1581
1615
  `scale thirds at degree ${degree} produce unsupported intervals [${offsets.join(", ")}]`
1582
1616
  );
1583
1617
  }
1584
- return { rootPc, quality, intervals: offsets };
1618
+ return makeSpelledChord(rootPc, quality, { rootSpelling: keySpelling(rootPc, resolved) });
1585
1619
  }
1586
1620
  function diatonicTriad(degree, key) {
1587
- return stackThirds(degree, toKeyScale(key), 3);
1621
+ return stackThirds(degree, resolveKey(key), 3);
1588
1622
  }
1589
1623
  function diatonicSeventh(degree, key) {
1590
- return stackThirds(degree, toKeyScale(key), 4);
1624
+ return stackThirds(degree, resolveKey(key), 4);
1591
1625
  }
1592
1626
 
1593
1627
  // src/theory/spelling/index.ts
@@ -1686,7 +1720,8 @@ function capChromaticAccidentals(preferred, alternative) {
1686
1720
  var DIATONIC_LETTER_COUNT = 7;
1687
1721
  var MAX_JOINT_TONE_COUNT = DIATONIC_LETTER_COUNT + 1;
1688
1722
  var MAJOR_SEMITONES = [0, 2, 4, 5, 7, 9, 11];
1689
- var IMPERFECT_DEGREES = /* @__PURE__ */ new Set([1, 2, 5, 6]);
1723
+ var IMPERFECT_DEGREE_LIST = [1, 2, 5, 6];
1724
+ var IMPERFECT_DEGREES = new Set(IMPERFECT_DEGREE_LIST);
1690
1725
  var DIATONIC_MODE_OFFSETS2 = MAJOR_SEMITONES.map(
1691
1726
  (tonic, mode) => MAJOR_SEMITONES.map((_, degree) => pitchClassOf((MAJOR_SEMITONES[(mode + degree) % 7] ?? 0) - tonic))
1692
1727
  );
@@ -1869,9 +1904,10 @@ function spellByKey(pc, tonic, key) {
1869
1904
  return preferred;
1870
1905
  }
1871
1906
  }
1872
- const belowLetter = diatonicLetterOf([0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6][pitchClassOf(pc)] ?? 0);
1907
+ const belowLetter = diatonicLetterOf(LETTER_BELOW_PITCH_CLASS[pitchClassOf(pc)] ?? 0);
1873
1908
  return { letter: belowLetter, alter: alterFor(belowLetter, pc) };
1874
1909
  }
1910
+ var LETTER_BELOW_PITCH_CLASS = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6];
1875
1911
  var CHORD_TONE_LETTER_STEPS = {
1876
1912
  0: 0,
1877
1913
  // root
@@ -1926,10 +1962,10 @@ function neighbourSpelling(neighbour, gap, letterStep, pc, tonic, key) {
1926
1962
  }
1927
1963
  return letterFor(spellByKey(pitchClassOf(neighbour), tonic, key), letterStep, pc);
1928
1964
  }
1929
- function refineByContext(pc, tonic, key, context, keySpelling) {
1965
+ function refineByContext(pc, tonic, key, context, keySpelling2) {
1930
1966
  const tonicPc = pitchClassOf(naturalPitchClassOf(tonic.letter) + tonic.alter);
1931
1967
  if (pitchClassOf(pc) === tonicPc || isScaleTone(pc, key)) {
1932
- return keySpelling;
1968
+ return keySpelling2;
1933
1969
  }
1934
1970
  const { previous, next } = context;
1935
1971
  const candidates = [
@@ -1952,7 +1988,7 @@ function refineByContext(pc, tonic, key, context, keySpelling) {
1952
1988
  return candidate;
1953
1989
  }
1954
1990
  }
1955
- return keySpelling;
1991
+ return keySpelling2;
1956
1992
  }
1957
1993
  function spellPitchClass(pc, tonic, key, context) {
1958
1994
  const note = toNoteData(tonic);
@@ -1962,8 +1998,8 @@ function spellPitchClass(pc, tonic, key, context) {
1962
1998
  return spelledPitchClass(pc, note, scale, context);
1963
1999
  }
1964
2000
  function spelledPitchClass(pc, tonic, key, context) {
1965
- const keySpelling = spellByKey(pc, tonic, key);
1966
- return context === void 0 ? keySpelling : refineByContext(pc, tonic, key, context, keySpelling);
2001
+ const keySpelling2 = spellByKey(pc, tonic, key);
2002
+ return context === void 0 ? keySpelling2 : refineByContext(pc, tonic, key, context, keySpelling2);
1967
2003
  }
1968
2004
  function spellScale(tonic, key) {
1969
2005
  const note = toNoteData(tonic);
@@ -1980,6 +2016,7 @@ function spellPitchClasses(pcs, tonic, key) {
1980
2016
  (pc, index) => spelledPitchClass(assertPitchClass(pc, `pcs[${index}]`), note, scale)
1981
2017
  );
1982
2018
  }
2019
+ var SIMPLE_LETTER_OFFSETS = [0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6];
1983
2020
  function chordLetterOffset(interval, chord) {
1984
2021
  switch (interval) {
1985
2022
  case 0:
@@ -1994,7 +2031,7 @@ function chordLetterOffset(interval, chord) {
1994
2031
  case 4:
1995
2032
  return 2;
1996
2033
  case 15:
1997
- return chord.intervals.some((i) => i === 3 || i === 4) ? 1 : 2;
2034
+ return 1;
1998
2035
  case 5:
1999
2036
  case 16:
2000
2037
  case 17:
@@ -2018,7 +2055,7 @@ function chordLetterOffset(interval, chord) {
2018
2055
  default: {
2019
2056
  const octaves = Math.floor(Math.max(0, interval) / 12);
2020
2057
  const simple = pitchClassOf(interval);
2021
- const simpleOffset = [0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6][simple] ?? 0;
2058
+ const simpleOffset = SIMPLE_LETTER_OFFSETS[simple] ?? 0;
2022
2059
  return octaves * 7 + simpleOffset;
2023
2060
  }
2024
2061
  }
@@ -2527,17 +2564,21 @@ function assertSafetyContext(q) {
2527
2564
  throw new InvalidInputError("vocalLow must not exceed vocalHigh");
2528
2565
  }
2529
2566
  if (!Array.isArray(q.otherVoices)) {
2530
- throw new InvalidInputError(`otherVoices must be an array; received ${typeof q.otherVoices}`);
2567
+ throw new InvalidInputError(
2568
+ `otherVoices must be an array; received ${describeRejected(q.otherVoices)}`
2569
+ );
2531
2570
  }
2532
2571
  assertGenerationBudget(q.otherVoices.length, "other voices");
2533
2572
  for (let index = 0; index < q.otherVoices.length; index += 1) {
2534
- const voice = q.otherVoices[index];
2535
- if (!voice) continue;
2573
+ const voice = assertRecord(q.otherVoices[index], `otherVoices[${index}]`);
2536
2574
  assertMidiPitch(voice.pitch, `otherVoices[${index}].pitch`);
2537
2575
  if (voice.prevPitch !== void 0) {
2538
2576
  assertMidiPitch(voice.prevPitch, `otherVoices[${index}].prevPitch`);
2539
2577
  }
2540
2578
  }
2579
+ if (q.chord !== null) {
2580
+ assertRecord(q.chord, "chord");
2581
+ }
2541
2582
  }
2542
2583
  function evaluateInternal(q, collectSuggestions, collectPartners) {
2543
2584
  let reasons = 0;
@@ -2925,6 +2966,19 @@ var ADDED_DEGREES = {
2925
2966
  };
2926
2967
  var ALTERABLE = { "5": 5, "9": 9, "11": 11, "13": 13 };
2927
2968
  var OMITTABLE = { "1": 1, "3": 3, "5": 5 };
2969
+ var SUSPENSION_TOKENS = [
2970
+ ["sus2", "sus2"],
2971
+ ["sus4", "sus4"],
2972
+ ["sus", "sus4"]
2973
+ ];
2974
+ var OMISSION_TOKENS = [
2975
+ ["omit", 4],
2976
+ ["no", 2]
2977
+ ];
2978
+ var ACCIDENTAL_TOKENS = [
2979
+ ["b", -1],
2980
+ ["#", 1]
2981
+ ];
2928
2982
  function alterationKey(degree, alter) {
2929
2983
  return `${degree}:${alter}`;
2930
2984
  }
@@ -2984,11 +3038,7 @@ function stackTo(draft, top) {
2984
3038
  }
2985
3039
  }
2986
3040
  function applyFigure(draft, text, at) {
2987
- const base = matchToken(text, at, [
2988
- ["sus2", "sus2"],
2989
- ["sus4", "sus4"],
2990
- ["sus", "sus4"]
2991
- ]);
3041
+ const base = matchToken(text, at, SUSPENSION_TOKENS);
2992
3042
  if (base !== void 0) {
2993
3043
  draft.base = base.value;
2994
3044
  return base.next;
@@ -3007,10 +3057,7 @@ function applyFigure(draft, text, at) {
3007
3057
  draft.additions.add(degree2.value);
3008
3058
  return degree2.next;
3009
3059
  }
3010
- const omitted = matchToken(text, at, [
3011
- ["omit", 4],
3012
- ["no", 2]
3013
- ]);
3060
+ const omitted = matchToken(text, at, OMISSION_TOKENS);
3014
3061
  if (omitted !== void 0) {
3015
3062
  const degree2 = matchNumberToken(text, omitted.next, OMITTABLE);
3016
3063
  if (degree2 === void 0) {
@@ -3019,10 +3066,7 @@ function applyFigure(draft, text, at) {
3019
3066
  draft.omissions.add(degree2.value);
3020
3067
  return degree2.next;
3021
3068
  }
3022
- const accidental = matchToken(text, at, [
3023
- ["b", -1],
3024
- ["#", 1]
3025
- ]);
3069
+ const accidental = matchToken(text, at, ACCIDENTAL_TOKENS);
3026
3070
  const from = accidental?.next ?? at;
3027
3071
  const degree = matchNumberToken(text, from, ALTERABLE);
3028
3072
  if (degree === void 0) {
@@ -3203,7 +3247,7 @@ function bassNote(text, system) {
3203
3247
  }
3204
3248
  return bareNote(text, system);
3205
3249
  }
3206
- function makeSpelledChord(root, spec, bass) {
3250
+ function chordFromSymbolParts(root, spec, bass) {
3207
3251
  const rooted = { ...spec, rootPc: noteToPitchClass(root) };
3208
3252
  if (bass !== void 0) {
3209
3253
  rooted.bassPc = noteToPitchClass(bass);
@@ -3221,11 +3265,11 @@ function chordAfterRoot(root, rest, system) {
3221
3265
  const slashSpec = suffixSpec(rest.slice(0, slashIndex));
3222
3266
  const bass = slashSpec === void 0 ? void 0 : bassNote(rest.slice(slashIndex + 1), system);
3223
3267
  if (slashSpec !== void 0 && bass !== void 0) {
3224
- return makeSpelledChord(root, slashSpec, bass);
3268
+ return chordFromSymbolParts(root, slashSpec, bass);
3225
3269
  }
3226
3270
  }
3227
3271
  const spec = suffixSpec(rest);
3228
- return spec === void 0 ? void 0 : makeSpelledChord(root, spec);
3272
+ return spec === void 0 ? void 0 : chordFromSymbolParts(root, spec);
3229
3273
  }
3230
3274
 
3231
3275
  // src/theory/symbol/suffix.ts
@@ -3302,7 +3346,11 @@ var BASE_SUFFIX = {
3302
3346
  sus4: "sus4",
3303
3347
  power: "5"
3304
3348
  };
3305
- var SEVENTH_SUFFIX = { maj7: "maj7", min7: "7", dim7: "dim7" };
3349
+ var SEVENTH_SUFFIX = {
3350
+ maj7: "maj7",
3351
+ min7: "7",
3352
+ dim7: "dim7"
3353
+ };
3306
3354
  var MAJOR_SEVENTH_AFTER_BASE = "Maj7";
3307
3355
  function builtCoreSuffix(spec) {
3308
3356
  const base = BASE_SUFFIX[spec.base];
@@ -3421,12 +3469,12 @@ function tryParseChordSymbol(text, opts) {
3421
3469
  }
3422
3470
  function formatChordSymbol(chord, opts) {
3423
3471
  const data = toChordData(chord);
3424
- if (!Object.hasOwn(CANONICAL_SUFFIX, data.quality)) {
3472
+ if (typeof data.quality !== "string" || !Object.hasOwn(CANONICAL_SUFFIX, data.quality)) {
3425
3473
  throw new InvalidInputError(`Unknown chord quality: ${String(data.quality)}`);
3426
3474
  }
3427
3475
  const asked = assertOptions(opts, "opts");
3428
3476
  const system = asked.system ?? DEFAULT_SYSTEM;
3429
- const flats = asked.flats;
3477
+ const flats = symbolFlats(asked.flats);
3430
3478
  const rootPc = pitchClassOf(data.rootPc);
3431
3479
  const rootHint = hintFor(data.rootSpelling, rootPc);
3432
3480
  const rootSpelling = rootSpellingFor(rootPc, rootHint, flats);
@@ -3441,7 +3489,7 @@ function formatChordSymbol(chord, opts) {
3441
3489
  (degreeOfInterval(pitchClassOf(bassPc - rootPc), data) ?? 1) - 1,
3442
3490
  bassPc
3443
3491
  ) : void 0;
3444
- const bassName = rootHint !== void 0 && bassHint !== void 0 ? symbolName(
3492
+ const bassName = flats !== void 0 && rootHint !== void 0 && bassHint !== void 0 ? symbolName(
3445
3493
  carriedBass(
3446
3494
  bassHint,
3447
3495
  rootHint,
@@ -3455,6 +3503,9 @@ function formatChordSymbol(chord, opts) {
3455
3503
  }
3456
3504
  return symbol;
3457
3505
  }
3506
+ function symbolFlats(flats) {
3507
+ return flats === void 0 ? void 0 : assertFlag(flats, "flats");
3508
+ }
3458
3509
  function transposeChordSpellings(spellings, semitones, flats) {
3459
3510
  const byLetter = (spelling) => spelling === void 0 ? void 0 : bareSpelling(transposeNote(spelling, semitones));
3460
3511
  const movedRoot = byLetter(spellings.rootSpelling);
@@ -3505,7 +3556,7 @@ function transposeChordSymbol(text, semitones, opts) {
3505
3556
  if (chord.bassPc !== void 0) {
3506
3557
  transposed.bassPc = pitchClassOf(chord.bassPc + semitones);
3507
3558
  }
3508
- const spellings = transposeChordSpellings(chord, semitones, opts?.flats);
3559
+ const spellings = transposeChordSpellings(chord, semitones, symbolFlats(opts?.flats));
3509
3560
  if (spellings.rootSpelling !== void 0) {
3510
3561
  transposed.rootSpelling = spellings.rootSpelling;
3511
3562
  }
@@ -3552,11 +3603,11 @@ function toChordData(value) {
3552
3603
  return parseChordSymbol(value);
3553
3604
  }
3554
3605
  if (typeof value === "object" && value !== null) {
3555
- const data = "toJSON" in value && typeof value.toJSON === "function" ? value.toJSON() : value;
3606
+ const data = "toJSON" in value && typeof value.toJSON === "function" ? "data" in value && typeof value.data === "object" && value.data !== null ? value.data : value.toJSON() : value;
3556
3607
  return normalizedChord(data);
3557
3608
  }
3558
3609
  throw new InvalidInputError(
3559
- `chord must be a chord symbol or chord data; received ${typeof value}`
3610
+ `chord must be a chord symbol or chord data; received ${describeRejected(value)}`
3560
3611
  );
3561
3612
  }
3562
3613
 
@@ -3567,6 +3618,9 @@ function hasInterval(chord, semitones) {
3567
3618
  function isDiatonicChord(chord, key) {
3568
3619
  return chordPitchClasses(chord).every((pc) => isScaleTone(pc, key));
3569
3620
  }
3621
+ function isNativeDominantSeventh(rootPc, key) {
3622
+ return isDiatonicChord(makeChord(rootPc, "dom7"), key);
3623
+ }
3570
3624
  function hasMajorThird(chord) {
3571
3625
  return hasInterval(chord, 4);
3572
3626
  }
@@ -3627,10 +3681,23 @@ function appliedDominantTarget(rootPc, key) {
3627
3681
  (degree) => degree.degreeNumber !== 1 && degree.rootPc === target
3628
3682
  ) ?? null;
3629
3683
  }
3684
+ function cadencesThroughRaisedSeventh(key) {
3685
+ return isMinorMask(key.modeMask12) && scaleSystemOf(key) === "common-practice";
3686
+ }
3630
3687
  function leadingTonePcOf(key) {
3631
3688
  return pitchClassOf(key.rootPc - 1);
3632
3689
  }
3633
- var LEADING_TONE_QUALITIES = /* @__PURE__ */ new Set(["dim", "dim7", "m7b5"]);
3690
+ var LEADING_TONE_QUALITY_NAMES = ["dim", "dim7", "m7b5"];
3691
+ var LEADING_TONE_QUALITIES = new Set(
3692
+ LEADING_TONE_QUALITY_NAMES
3693
+ );
3694
+ function appliedChordTarget(chord, key) {
3695
+ const dominant = hasDominantSonority(chord);
3696
+ if (!dominant && !LEADING_TONE_QUALITIES.has(chord.quality)) {
3697
+ return null;
3698
+ }
3699
+ return appliedDominantTarget(chord.rootPc + (dominant ? 5 : 1), key);
3700
+ }
3634
3701
  function soundsDiminishedTriad(chord) {
3635
3702
  return hasInterval(chord, 3) && hasInterval(chord, 6) && !hasInterval(chord, 7);
3636
3703
  }
@@ -3757,12 +3824,16 @@ export {
3757
3824
  scaleSystemOf,
3758
3825
  supportsFunctionalHarmony,
3759
3826
  chordSpecIntervals,
3827
+ QUALITY_INTERVALS,
3828
+ chordSpecForQuality,
3760
3829
  chordSpecQuality,
3761
3830
  chordQualities,
3762
3831
  assertChordQuality,
3763
3832
  assertChord,
3764
3833
  chordFromDegree,
3765
3834
  makeChord,
3835
+ makeSpelledChord,
3836
+ keySpelling,
3766
3837
  chordFromSpec,
3767
3838
  chordSpecOf,
3768
3839
  chordFromSpan,
@@ -3800,22 +3871,24 @@ export {
3800
3871
  evaluateSafety,
3801
3872
  enumerateSafePitches,
3802
3873
  toChordData,
3874
+ spellingAbove,
3803
3875
  parseChordSymbol,
3804
3876
  tryParseChordSymbol,
3805
3877
  formatChordSymbol,
3806
3878
  transposeChordSpellings,
3807
3879
  transposeChordSymbol,
3808
3880
  isDiatonicChord,
3881
+ isNativeDominantSeventh,
3809
3882
  hasMajorThird,
3810
3883
  soundsDominantSeventh,
3811
- hasDominantSonority,
3812
3884
  isDominantChordOf,
3813
3885
  isNeapolitanChordOf,
3814
3886
  heptatonicFrameOf,
3815
3887
  tonicizableDegrees,
3816
- appliedDominantTarget,
3888
+ cadencesThroughRaisedSeventh,
3817
3889
  leadingTonePcOf,
3818
3890
  LEADING_TONE_QUALITIES,
3891
+ appliedChordTarget,
3819
3892
  isLeadingToneChordOf,
3820
3893
  isFunctioningLeadingTone,
3821
3894
  seventhPcOf,
@@ -3823,4 +3896,4 @@ export {
3823
3896
  isDescendingStep,
3824
3897
  isFrustratedLeadingTone
3825
3898
  };
3826
- //# sourceMappingURL=chunk-RFIBF2RR.js.map
3899
+ //# sourceMappingURL=chunk-H7FVMBUC.js.map