@libraz/libcantus 0.9.4 → 0.9.5

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 (120) hide show
  1. package/README.md +27 -18
  2. package/README_ja.md +268 -0
  3. package/dist/analyze/index.cjs +7 -7
  4. package/dist/analyze/index.d.cts +9 -99
  5. package/dist/analyze/index.d.ts +9 -99
  6. package/dist/analyze/index.js +6 -6
  7. package/dist/{chunk-36N73KZ3.cjs → chunk-2A2QMA74.cjs} +80 -65
  8. package/dist/chunk-2A2QMA74.cjs.map +1 -0
  9. package/dist/{chunk-YLEMGEFH.js → chunk-2PTFGVD7.js} +50 -35
  10. package/dist/chunk-2PTFGVD7.js.map +1 -0
  11. package/dist/{chunk-ZEJR4TKL.cjs → chunk-357ZDPVU.cjs} +36 -47
  12. package/dist/chunk-357ZDPVU.cjs.map +1 -0
  13. package/dist/{chunk-EHVEBWM5.js → chunk-3KX76RLG.js} +136 -37
  14. package/dist/chunk-3KX76RLG.js.map +1 -0
  15. package/dist/{chunk-RC6GBMCD.cjs → chunk-4VZ5MZAM.cjs} +155 -111
  16. package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
  17. package/dist/{chunk-RK35RIN7.js → chunk-7EONYVI5.js} +115 -28
  18. package/dist/chunk-7EONYVI5.js.map +1 -0
  19. package/dist/{chunk-GQSGS2N4.js → chunk-7JEP3MFY.js} +5 -16
  20. package/dist/chunk-7JEP3MFY.js.map +1 -0
  21. package/dist/{chunk-7NKDPF74.js → chunk-AID75RN3.js} +203 -104
  22. package/dist/chunk-AID75RN3.js.map +1 -0
  23. package/dist/{chunk-FNDWXRRI.cjs → chunk-BSLTLGM3.cjs} +55 -37
  24. package/dist/chunk-BSLTLGM3.cjs.map +1 -0
  25. package/dist/{chunk-NP4NA2FB.cjs → chunk-CU6VG2YM.cjs} +39 -36
  26. package/dist/chunk-CU6VG2YM.cjs.map +1 -0
  27. package/dist/{chunk-NYYWMV2C.js → chunk-DZMHIGI4.js} +27 -17
  28. package/dist/chunk-DZMHIGI4.js.map +1 -0
  29. package/dist/{chunk-I24MV65U.js → chunk-EDOTEABK.js} +18 -15
  30. package/dist/{chunk-I24MV65U.js.map → chunk-EDOTEABK.js.map} +1 -1
  31. package/dist/{chunk-NKIBQ6PT.js → chunk-HN2OEUN2.js} +7 -4
  32. package/dist/chunk-HN2OEUN2.js.map +1 -0
  33. package/dist/{chunk-D2FAJBYL.js → chunk-KBTTEX7Q.js} +30 -12
  34. package/dist/chunk-KBTTEX7Q.js.map +1 -0
  35. package/dist/{chunk-CAN76NCU.cjs → chunk-KQN2ZYND.cjs} +147 -102
  36. package/dist/chunk-KQN2ZYND.cjs.map +1 -0
  37. package/dist/{chunk-4HLLZPXT.cjs → chunk-MPTVJIVG.cjs} +350 -251
  38. package/dist/chunk-MPTVJIVG.cjs.map +1 -0
  39. package/dist/{chunk-RUMU3UA6.cjs → chunk-MTMZTMWM.cjs} +12 -12
  40. package/dist/{chunk-RUMU3UA6.cjs.map → chunk-MTMZTMWM.cjs.map} +1 -1
  41. package/dist/{chunk-KMHOICIY.js → chunk-NNB67XA2.js} +90 -45
  42. package/dist/chunk-NNB67XA2.js.map +1 -0
  43. package/dist/{chunk-LW53AHQU.cjs → chunk-OL3IHHSJ.cjs} +228 -141
  44. package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
  45. package/dist/{chunk-7MGZ7D62.cjs → chunk-PI3UXRWK.cjs} +43 -13
  46. package/dist/chunk-PI3UXRWK.cjs.map +1 -0
  47. package/dist/{chunk-XEBG3G53.js → chunk-R2VVFAIN.js} +42 -12
  48. package/dist/chunk-R2VVFAIN.js.map +1 -0
  49. package/dist/{chunk-Q6OP4U43.cjs → chunk-WFDXOLUB.cjs} +50 -40
  50. package/dist/chunk-WFDXOLUB.cjs.map +1 -0
  51. package/dist/{chunk-NVE34YGZ.cjs → chunk-WTAN4ACG.cjs} +212 -113
  52. package/dist/chunk-WTAN4ACG.cjs.map +1 -0
  53. package/dist/{chunk-FCANXBAT.cjs → chunk-YFJXS3AU.cjs} +8 -5
  54. package/dist/chunk-YFJXS3AU.cjs.map +1 -0
  55. package/dist/{chunk-Q2XUP227.js → chunk-YUE6TJ4M.js} +89 -45
  56. package/dist/chunk-YUE6TJ4M.js.map +1 -0
  57. package/dist/{chunk-HKGYCQ34.js → chunk-ZGLJM4A4.js} +2 -2
  58. package/dist/core/index.cjs +15 -5
  59. package/dist/core/index.cjs.map +1 -1
  60. package/dist/core/index.d.cts +14 -4
  61. package/dist/core/index.d.ts +14 -4
  62. package/dist/core/index.js +14 -4
  63. package/dist/{function-DNugAOrt.d.cts → function-CxixTB02.d.cts} +98 -7
  64. package/dist/{function-BtiZb9i9.d.ts → function-DTCt675z.d.ts} +98 -7
  65. package/dist/generate/index.cjs +10 -10
  66. package/dist/generate/index.d.cts +21 -21
  67. package/dist/generate/index.d.ts +21 -21
  68. package/dist/generate/index.js +9 -9
  69. package/dist/{index-tJhkmQgE.d.cts → index-B2Ulxxpo.d.cts} +3 -2
  70. package/dist/{index-Be74mcon.d.cts → index-BkYJ0xMq.d.cts} +2 -2
  71. package/dist/{index-6VR8-kit.d.ts → index-BuaB-ypt.d.ts} +3 -2
  72. package/dist/{index-70p168VM.d.ts → index-CAN7v-kw.d.ts} +9 -2
  73. package/dist/{index-C83mZI1P.d.ts → index-COw1Ky9g.d.ts} +1 -1
  74. package/dist/{index-tSQh-xwY.d.ts → index-Cfx8xgH0.d.ts} +1 -1
  75. package/dist/{index-C5UBbQ4G.d.ts → index-DRtIhlJj.d.ts} +2 -2
  76. package/dist/{index-BtXT6UQ0.d.cts → index-alrRninU.d.cts} +9 -2
  77. package/dist/{index-y_ak9_Hi.d.cts → index-oTQbYYIs.d.cts} +1 -1
  78. package/dist/{index-OG65ocOr.d.ts → index-tR9jTn26.d.cts} +4 -1
  79. package/dist/{index-OG65ocOr.d.cts → index-tR9jTn26.d.ts} +4 -1
  80. package/dist/{index-D6b6na69.d.cts → index-x2vcsduE.d.cts} +1 -1
  81. package/dist/index.cjs +24 -14
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.d.cts +11 -11
  84. package/dist/index.d.ts +11 -11
  85. package/dist/index.js +23 -13
  86. package/dist/model/index.cjs +7 -7
  87. package/dist/model/index.d.cts +48 -16
  88. package/dist/model/index.d.ts +48 -16
  89. package/dist/model/index.js +6 -6
  90. package/dist/{styled-DIRkk9Uz.d.ts → styled-Bl0ZK28h.d.ts} +40 -3
  91. package/dist/{styled-DJbkJQfj.d.cts → styled-Crv6SA4S.d.cts} +40 -3
  92. package/dist/theory/index.cjs +6 -6
  93. package/dist/theory/index.d.cts +9 -9
  94. package/dist/theory/index.d.ts +9 -9
  95. package/dist/theory/index.js +5 -5
  96. package/package.json +12 -9
  97. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  98. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  99. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  100. package/dist/chunk-7NKDPF74.js.map +0 -1
  101. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  102. package/dist/chunk-D2FAJBYL.js.map +0 -1
  103. package/dist/chunk-EHVEBWM5.js.map +0 -1
  104. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  105. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  106. package/dist/chunk-GQSGS2N4.js.map +0 -1
  107. package/dist/chunk-KMHOICIY.js.map +0 -1
  108. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  109. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  110. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  111. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  112. package/dist/chunk-NYYWMV2C.js.map +0 -1
  113. package/dist/chunk-Q2XUP227.js.map +0 -1
  114. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  115. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  116. package/dist/chunk-RK35RIN7.js.map +0 -1
  117. package/dist/chunk-XEBG3G53.js.map +0 -1
  118. package/dist/chunk-YLEMGEFH.js.map +0 -1
  119. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  120. /package/dist/{chunk-HKGYCQ34.js.map → chunk-ZGLJM4A4.js.map} +0 -0
@@ -9,49 +9,54 @@
9
9
 
10
10
 
11
11
 
12
- var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
12
+ var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
13
13
 
14
14
 
15
15
 
16
16
 
17
17
 
18
18
 
19
- var _chunk7MGZ7D62cjs = require('./chunk-7MGZ7D62.cjs');
19
+
20
+
21
+ var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
20
22
 
21
23
  // src/analyze/detect/index.ts
22
24
  function uniquePitchClasses(pitches) {
23
- return [...new Set(pitches.map(_chunk7MGZ7D62cjs.pitchClassOf))].sort((a, b) => a - b);
25
+ return [...new Set(pitches.map(_chunkPI3UXRWKcjs.pitchClassOf))].sort((a, b) => a - b);
24
26
  }
25
- function assertPitches(pitches) {
26
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, pitches.length, "detection pitches");
27
+ function assertPitches(pitches, name, budget = void 0) {
28
+ _chunkPI3UXRWKcjs.assertGenerationBudget.call(void 0, pitches.length, name, budget);
27
29
  for (let index = 0; index < pitches.length; index += 1) {
28
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, _nullishCoalesce(pitches[index], () => ( Number.NaN)), `pitches[${index}]`);
30
+ _chunkPI3UXRWKcjs.assertMidiPitch.call(void 0, _nullishCoalesce(pitches[index], () => ( Number.NaN)), `pitches[${index}]`);
29
31
  }
30
32
  }
31
33
  function detectChord(pitches, opts = {}) {
32
- assertPitches(pitches);
33
- if (opts.bassPc !== void 0) _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, opts.bassPc, "bassPc");
34
+ assertPitches(pitches, "chord detection pitches");
35
+ if (opts.bassPc !== void 0) _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, opts.bassPc, "bassPc");
34
36
  const input = uniquePitchClasses(pitches);
35
37
  if (input.length === 0) {
36
38
  return [];
37
39
  }
38
40
  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 ? _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, opts.bassPc) : inputKind === "midi" ? _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
41
+ const bassPc = opts.bassPc !== void 0 ? _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, opts.bassPc) : inputKind === "midi" ? _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pitches.reduce((lowest, pitch) => Math.min(lowest, pitch), Infinity)) : void 0;
40
42
  const inputSet = new Set(input);
41
43
  const matches = [];
42
- const qualities = _chunkFNDWXRRIcjs.chordQualities.call(void 0, );
44
+ const qualities = _chunkBSLTLGM3cjs.chordQualities.call(void 0, );
45
+ const toneCounts = new Map(
46
+ qualities.map((quality) => [quality, _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, 0, quality)).length])
47
+ );
43
48
  for (const rootPc of input) {
44
49
  for (const quality of qualities) {
45
- const chord = _chunkFNDWXRRIcjs.makeChord.call(void 0, rootPc, quality);
46
- const tones = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
50
+ const chord = _chunkBSLTLGM3cjs.makeChord.call(void 0, rootPc, quality);
51
+ const tones = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord);
47
52
  const toneSet = new Set(tones);
48
53
  const missingPcs = tones.filter((pc) => !inputSet.has(pc));
49
- const fifthPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc + 7);
54
+ const fifthPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc + 7);
50
55
  if (missingPcs.length > 1 || missingPcs.length === 1 && missingPcs[0] !== fifthPc) {
51
56
  continue;
52
57
  }
53
58
  const extraPcs = input.filter((pc) => !toneSet.has(pc));
54
- const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc + iv) === bassPc);
59
+ const bassIndex = bassPc === void 0 ? -1 : chord.intervals.findIndex((iv) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc + iv) === bassPc);
55
60
  const inversion = bassPc === void 0 || bassIndex < 0 ? null : bassIndex;
56
61
  const match = {
57
62
  rootPc,
@@ -77,8 +82,8 @@ function detectChord(pitches, opts = {}) {
77
82
  if (a.inversion === 0 !== (b.inversion === 0)) {
78
83
  return a.inversion === 0 ? -1 : 1;
79
84
  }
80
- const aSize = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, a.rootPc, a.quality)).length;
81
- const bSize = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, b.rootPc, b.quality)).length;
85
+ const aSize = _nullishCoalesce(toneCounts.get(a.quality), () => ( 0));
86
+ const bSize = _nullishCoalesce(toneCounts.get(b.quality), () => ( 0));
82
87
  return bSize - aSize;
83
88
  });
84
89
  return matches;
@@ -88,39 +93,36 @@ function detectChordBest(pitches, opts = {}) {
88
93
  if (!best) {
89
94
  return null;
90
95
  }
91
- return _chunkFNDWXRRIcjs.makeChord.call(void 0, best.rootPc, best.quality, best.bassPc);
96
+ return _chunkBSLTLGM3cjs.makeChord.call(void 0, best.rootPc, best.quality, best.bassPc);
92
97
  }
93
98
  var MINOR_VARIANTS = [
94
- { variant: "natural", mask: _chunkFNDWXRRIcjs.NATURAL_MINOR_MASK },
95
- { variant: "harmonic", mask: _chunkFNDWXRRIcjs.HARMONIC_MINOR_MASK },
96
- { variant: "melodic", mask: _chunkFNDWXRRIcjs.MELODIC_MINOR_MASK }
99
+ { variant: "natural", mask: _chunkBSLTLGM3cjs.NATURAL_MINOR_MASK },
100
+ { variant: "harmonic", mask: _chunkBSLTLGM3cjs.HARMONIC_MINOR_MASK },
101
+ { variant: "melodic", mask: _chunkBSLTLGM3cjs.MELODIC_MINOR_MASK }
97
102
  ];
98
- var MAJOR_VARIANTS = [{ variant: "major", mask: _chunkFNDWXRRIcjs.MAJOR_MASK }];
103
+ var MAJOR_VARIANTS = [{ variant: "major", mask: _chunkBSLTLGM3cjs.MAJOR_MASK }];
99
104
  function detectKey(pitches, opts = {}) {
100
- assertPitches(pitches);
101
- const input = uniquePitchClasses(pitches);
102
- if (input.length === 0) {
103
- return [];
104
- }
105
+ assertPitches(pitches, "key detection pitches", opts.budget);
105
106
  const weights = opts.weights;
106
107
  if (weights !== void 0 && weights.length !== pitches.length) {
107
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("weights must have one entry per pitch");
108
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("weights must have one entry per pitch");
108
109
  }
109
110
  const counts = /* @__PURE__ */ new Map();
110
111
  let total = 0;
111
112
  for (let index = 0; index < pitches.length; index += 1) {
112
113
  const weight = weights === void 0 ? 1 : _nullishCoalesce(weights[index], () => ( 0));
113
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, weight, `weights[${index}]`);
114
+ _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, weight, `weights[${index}]`);
114
115
  if (weight <= 0) {
115
116
  continue;
116
117
  }
117
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _nullishCoalesce(pitches[index], () => ( 0)));
118
+ const pc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _nullishCoalesce(pitches[index], () => ( 0)));
118
119
  counts.set(pc, (_nullishCoalesce(counts.get(pc), () => ( 0))) + weight);
119
120
  total += weight;
120
121
  }
121
122
  if (total === 0) {
122
123
  return [];
123
124
  }
125
+ const input = [...counts.keys()];
124
126
  const results = [];
125
127
  for (let tonic = 0; tonic < 12; tonic += 1) {
126
128
  for (const mode of ["major", "minor"]) {
@@ -163,11 +165,18 @@ function detectKey(pitches, opts = {}) {
163
165
  return results;
164
166
  }
165
167
  var DEFAULT_VELOCITY = 100;
166
- function detectKeyFromNotes(notes) {
168
+ function detectKeyFromNotes(notes, opts = {}) {
169
+ _chunkPI3UXRWKcjs.assertNoteEvents.call(void 0, notes, "key detection notes", {
170
+ allowNonPositiveDuration: true,
171
+ budget: opts.budget
172
+ });
167
173
  const sounding = notes.filter((note) => note.durationBeat > 0);
168
174
  return detectKey(
169
175
  sounding.map((note) => note.pitch),
170
- { weights: sounding.map((note) => note.durationBeat * (_nullishCoalesce(note.velocity, () => ( DEFAULT_VELOCITY)))) }
176
+ {
177
+ weights: sounding.map((note) => note.durationBeat * (_nullishCoalesce(note.velocity, () => ( DEFAULT_VELOCITY)))),
178
+ budget: opts.budget
179
+ }
171
180
  );
172
181
  }
173
182
  function detectKeyBest(pitches, opts = {}) {
@@ -176,34 +185,38 @@ function detectKeyBest(pitches, opts = {}) {
176
185
 
177
186
  // src/analyze/functional/internal.ts
178
187
  function degreeRootPc(degreeNumber, key) {
179
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key);
188
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key);
180
189
  if (tones.length === 0) {
181
- return _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
190
+ return _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
182
191
  }
183
192
  const index = ((degreeNumber - 1) % tones.length + tones.length) % tones.length;
184
- return _nullishCoalesce(tones[index], () => ( _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc)));
193
+ return _nullishCoalesce(tones[index], () => ( _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc)));
185
194
  }
186
195
  function loweredDegrees(key) {
187
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key);
196
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key);
188
197
  const out = /* @__PURE__ */ new Set();
189
198
  if (tones.length !== 7) {
190
199
  return out;
191
200
  }
192
- const major = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, { rootPc: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkFNDWXRRIcjs.MAJOR_MASK });
201
+ const major = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, { rootPc: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkBSLTLGM3cjs.MAJOR_MASK });
193
202
  for (let index = 0; index < 7; index += 1) {
194
203
  const tone = tones[index];
195
204
  const majorTone = major[index];
196
- if (tone !== void 0 && majorTone !== void 0 && _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, majorTone - tone) === 1) {
205
+ if (tone !== void 0 && majorTone !== void 0 && _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, majorTone - tone) === 1) {
197
206
  out.add(index + 1);
198
207
  }
199
208
  }
200
209
  return out;
201
210
  }
202
211
  function romanReference(key) {
203
- return _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key).length === 7 ? key : _chunkFNDWXRRIcjs.majorKey.call(void 0, _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc));
212
+ return _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).length === 7 ? key : _chunkBSLTLGM3cjs.majorKey.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc));
204
213
  }
205
214
  function isNeapolitan(chord, key) {
206
- return _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 1 && chord.quality === "maj";
215
+ return _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 1 && chord.quality === "maj";
216
+ }
217
+ function isAppliedDominantSonority(chord) {
218
+ const has = (semitones) => chord.intervals.some((i) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, i) === semitones);
219
+ return chord.quality === "maj" || has(4) && has(10);
207
220
  }
208
221
 
209
222
  // src/analyze/functional/roman.ts
@@ -255,6 +268,9 @@ var ROMAN_STYLE = {
255
268
  dim7: { lower: true, suffix: "o7" },
256
269
  m7b5: { lower: true, suffix: "\xF87" },
257
270
  minMaj7: { lower: true, suffix: "maj7" },
271
+ minMaj9: { lower: true, suffix: "maj9" },
272
+ minMaj11: { lower: true, suffix: "maj11" },
273
+ minMaj13: { lower: true, suffix: "maj13" },
258
274
  aug7: { lower: false, suffix: "+7" },
259
275
  augMaj7: { lower: false, suffix: "+maj7" },
260
276
  majb5: { lower: false, suffix: "b5" },
@@ -315,26 +331,33 @@ function parseInversion(figures) {
315
331
  function parseSimpleRoman(text, key) {
316
332
  const match = /^([b#]?)([iIvV]+)(.*)$/.exec(text.trim());
317
333
  if (!match) {
318
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
334
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
335
+ }
336
+ const glyphText = _nullishCoalesce(match[2], () => ( ""));
337
+ if (glyphText !== glyphText.toUpperCase() && glyphText !== glyphText.toLowerCase()) {
338
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Roman numeral must use one case: ${text}`);
319
339
  }
320
340
  const accidental = match[1] === "b" ? -1 : match[1] === "#" ? 1 : 0;
321
- const glyph = (_nullishCoalesce(match[2], () => ( ""))).toUpperCase();
341
+ const glyph = glyphText.toUpperCase();
322
342
  const degreeNumber = ROMAN_TO_DEGREE[glyph];
323
343
  if (degreeNumber === void 0) {
324
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
344
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Invalid Roman numeral: ${text}`);
325
345
  }
326
- const isUpper = (_nullishCoalesce(match[2], () => ( "")))[0] === _optionalChain([(_nullishCoalesce(match[2], () => ( ""))), 'access', _ => _[0], 'optionalAccess', _2 => _2.toUpperCase, 'call', _3 => _3()]);
346
+ const isUpper = glyphText[0] === _optionalChain([glyphText, 'access', _ => _[0], 'optionalAccess', _2 => _2.toUpperCase, 'call', _3 => _3()]);
327
347
  const suffix = _nullishCoalesce(match[3], () => ( ""));
328
348
  const frame = romanReference(key);
329
349
  const redundantFlat = accidental === -1 && loweredDegrees(frame).has(degreeNumber);
330
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, degreeRootPc(degreeNumber, frame) + (redundantFlat ? 0 : accidental));
350
+ const diatonicRootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0,
351
+ degreeRootPc(degreeNumber, frame) + (redundantFlat ? 0 : accidental)
352
+ );
353
+ const rootForQuality = (quality2) => accidental === 0 && degreeNumber === 7 && loweredDegrees(frame).has(7) && (quality2 === "dim" || quality2 === "dim7") ? _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, diatonicRootPc + 1) : diatonicRootPc;
331
354
  const canonical = SUFFIX_QUALITY.get(`${isUpper ? "u" : "l"}:${suffix}`);
332
355
  if (canonical !== void 0) {
333
- return { rootPc, quality: canonical, inversion: 0 };
356
+ return { rootPc: rootForQuality(canonical), quality: canonical, inversion: 0 };
334
357
  }
335
358
  const parsedSuffix = /^(o|°|dim|ø|\+|aug)?(maj7|M7)?(65|64|43|42|7|6|2)?$/.exec(suffix);
336
359
  if (!parsedSuffix) {
337
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
360
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`Unsupported suffix "${suffix}" in Roman numeral: ${text}`);
338
361
  }
339
362
  const qualityMarker = _nullishCoalesce(parsedSuffix[1], () => ( ""));
340
363
  const isDim = qualityMarker === "o" || qualityMarker === "\xB0" || qualityMarker === "dim";
@@ -343,7 +366,7 @@ function parseSimpleRoman(text, key) {
343
366
  const explicitMaj7 = parsedSuffix[2] !== void 0;
344
367
  const figures = _nullishCoalesce(parsedSuffix[3], () => ( ""));
345
368
  if (figures !== "" && !INVERSION_FIGURES.has(figures)) {
346
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
369
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
347
370
  `Unsupported figured-bass or extension "${figures}" in Roman numeral: ${text}`
348
371
  );
349
372
  }
@@ -369,23 +392,23 @@ function parseSimpleRoman(text, key) {
369
392
  } else {
370
393
  quality = base;
371
394
  }
372
- return { rootPc, quality, inversion };
395
+ return { rootPc: rootForQuality(quality), quality, inversion };
373
396
  }
374
397
  function chordFromParsed(parsed) {
375
- const chord = _chunkFNDWXRRIcjs.makeChord.call(void 0, parsed.rootPc, parsed.quality);
398
+ const chord = _chunkBSLTLGM3cjs.makeChord.call(void 0, parsed.rootPc, parsed.quality);
376
399
  if (parsed.inversion > 0 && parsed.inversion < chord.intervals.length) {
377
- chord.bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, parsed.rootPc + (_nullishCoalesce(chord.intervals[parsed.inversion], () => ( 0))));
400
+ chord.bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, parsed.rootPc + (_nullishCoalesce(chord.intervals[parsed.inversion], () => ( 0))));
378
401
  }
379
402
  return chord;
380
403
  }
381
404
  function romanToChord(text, key) {
382
- const trimmed = text.trim();
405
+ const trimmed = text.trim().replace(/(\d)\/(?=\d)/g, "$1");
383
406
  const slash = trimmed.indexOf("/");
384
407
  if (slash >= 0) {
385
408
  const applied = trimmed.slice(0, slash);
386
409
  const target = trimmed.slice(slash + 1);
387
410
  const targetRoot = parseSimpleRoman(target, key).rootPc;
388
- const localKey = _chunkFNDWXRRIcjs.majorKey.call(void 0, targetRoot);
411
+ const localKey = _chunkBSLTLGM3cjs.majorKey.call(void 0, targetRoot);
389
412
  return chordFromParsed(parseSimpleRoman(applied, localKey));
390
413
  }
391
414
  return chordFromParsed(parseSimpleRoman(trimmed, key));
@@ -394,14 +417,14 @@ function romanStyle(quality) {
394
417
  return ROMAN_STYLE[quality];
395
418
  }
396
419
  function romanSpelling(rootPc, key) {
397
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
398
- const diatonic = tones.indexOf(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc));
420
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
421
+ const diatonic = tones.indexOf(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc));
399
422
  if (diatonic >= 0) {
400
423
  return { degreeNumber: diatonic + 1, accidental: "" };
401
424
  }
402
425
  const lowered = loweredDegrees(romanReference(key));
403
426
  for (let i = 0; i < tones.length; i += 1) {
404
- if (lowered.has(i + 1) && _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc)) {
427
+ if (lowered.has(i + 1) && _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc)) {
405
428
  return { degreeNumber: i + 1, accidental: "#" };
406
429
  }
407
430
  }
@@ -410,16 +433,16 @@ function romanSpelling(rootPc, key) {
410
433
  if (degreeNumber2 === 1 || degreeNumber2 === 5) {
411
434
  continue;
412
435
  }
413
- if (_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) - 1) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc)) {
436
+ if (_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) - 1) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc)) {
414
437
  return { degreeNumber: degreeNumber2, accidental: "b" };
415
438
  }
416
439
  }
417
440
  for (let i = 0; i < tones.length; i += 1) {
418
- if (_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc)) {
441
+ if (_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[i], () => ( 0))) + 1) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc)) {
419
442
  return { degreeNumber: i + 1, accidental: "#" };
420
443
  }
421
444
  }
422
- const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc - key.rootPc)], () => ( [1, ""]));
445
+ const [degreeNumber, accidental] = _nullishCoalesce(OFFSET_SPELLING[_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, rootPc - key.rootPc)], () => ( [1, ""]));
423
446
  return { degreeNumber, accidental };
424
447
  }
425
448
  function baseMarker(quality) {
@@ -456,23 +479,19 @@ var SEVENTH_FIGURE_QUALITIES = /* @__PURE__ */ new Set([
456
479
  function numeralFor(degreeNumber, lower) {
457
480
  const glyph = ROMAN[degreeNumber - 1];
458
481
  if (glyph === void 0) {
459
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`no Roman numeral for scale degree ${degreeNumber}`);
482
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`no Roman numeral for scale degree ${degreeNumber}`);
460
483
  }
461
484
  return lower ? glyph.toLowerCase() : glyph;
462
485
  }
463
486
  function isDiatonicChord(chord, key) {
464
- return _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunkFNDWXRRIcjs.isScaleTone.call(void 0, pc, key));
465
- }
466
- function isAppliedDominantSonority(chord) {
467
- const has = (semitones) => chord.intervals.some((i) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, i) === semitones);
468
- return chord.quality === "maj" || has(4) && has(10);
487
+ return _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunkBSLTLGM3cjs.isScaleTone.call(void 0, pc, key));
469
488
  }
470
489
  var LEADING_TONE_QUALITIES = /* @__PURE__ */ new Set(["dim", "dim7", "m7b5"]);
471
490
  function degreeTriad(tones, index) {
472
491
  const root = _nullishCoalesce(tones[index], () => ( 0));
473
492
  return {
474
- third: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[(index + 2) % tones.length], () => ( 0))) - root),
475
- fifth: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[(index + 4) % tones.length], () => ( 0))) - root)
493
+ third: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[(index + 2) % tones.length], () => ( 0))) - root),
494
+ fifth: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, (_nullishCoalesce(tones[(index + 4) % tones.length], () => ( 0))) - root)
476
495
  };
477
496
  }
478
497
  function appliedTarget(chord, key) {
@@ -481,18 +500,15 @@ function appliedTarget(chord, key) {
481
500
  if (!dominant && !leadingTone) {
482
501
  return null;
483
502
  }
484
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
503
+ const tones = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, romanReference(key));
485
504
  for (let index = 1; index < tones.length; index += 1) {
486
505
  const targetRoot = tones[index];
487
506
  if (targetRoot === void 0) {
488
507
  continue;
489
508
  }
490
509
  const triad = degreeTriad(tones, index);
491
- if (triad.fifth === 6) {
492
- continue;
493
- }
494
- const expectedRoot = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, targetRoot + (dominant ? 7 : -1));
495
- if (expectedRoot === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc)) {
510
+ const expectedRoot = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, targetRoot + (dominant ? 7 : -1));
511
+ if (expectedRoot === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc)) {
496
512
  return { degreeNumber: index + 1, rootPc: targetRoot, lower: triad.third === 3 };
497
513
  }
498
514
  }
@@ -502,16 +518,17 @@ function chordToRoman(chord, key, opts = {}) {
502
518
  if (opts.applied === true && !isDiatonicChord(chord, key)) {
503
519
  const target = appliedTarget(chord, key);
504
520
  if (target !== null) {
505
- const local = chordToRoman(chord, _chunkFNDWXRRIcjs.majorKey.call(void 0, target.rootPc));
521
+ const local = chordToRoman(chord, _chunkBSLTLGM3cjs.majorKey.call(void 0, target.rootPc));
506
522
  return `${local}/${numeralFor(target.degreeNumber, target.lower)}`;
507
523
  }
508
524
  }
509
- const { degreeNumber, accidental } = romanSpelling(chord.rootPc, key);
525
+ const { degreeNumber, accidental: spelledAccidental } = romanSpelling(chord.rootPc, key);
510
526
  const { lower, suffix } = romanStyle(chord.quality);
511
527
  const cased = numeralFor(degreeNumber, lower);
528
+ const accidental = spelledAccidental === "#" && degreeNumber === 7 && loweredDegrees(romanReference(key)).has(7) && (chord.quality === "dim" || chord.quality === "dim7") ? "" : spelledAccidental;
512
529
  let inversion = 0;
513
530
  if (chord.bassPc !== void 0) {
514
- const idx = chord.intervals.findIndex((iv) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + iv) === chord.bassPc);
531
+ const idx = chord.intervals.findIndex((iv) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + iv) === chord.bassPc);
515
532
  if (idx > 0) {
516
533
  inversion = idx;
517
534
  }
@@ -526,14 +543,20 @@ function chordToRoman(chord, key, opts = {}) {
526
543
  }
527
544
 
528
545
  // src/analyze/functional/borrowed.ts
529
- function isHarmonicMinorAlteration(chord, key) {
530
- return isMinorKey(key) && isDiatonic(chord, { rootPc: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkFNDWXRRIcjs.HARMONIC_MINOR_MASK });
546
+ function isMinorScaleAlteration(chord, key) {
547
+ if (!isMinorKey(key)) return false;
548
+ const alteredKey = { rootPc: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkBSLTLGM3cjs.HARMONIC_MINOR_MASK };
549
+ if (isDiatonic(chord, alteredKey)) return true;
550
+ return _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 11 && isDiatonic(chord, { rootPc: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc), modeMask12: _chunkBSLTLGM3cjs.MELODIC_MINOR_MASK });
531
551
  }
532
552
  function isBorrowedChord(chord, key) {
533
553
  if (isDiatonic(chord, key)) {
534
554
  return false;
535
555
  }
536
- if (isHarmonicMinorAlteration(chord, key)) {
556
+ if (isNeapolitan(chord, key)) {
557
+ return true;
558
+ }
559
+ if (isMinorScaleAlteration(chord, key)) {
537
560
  return false;
538
561
  }
539
562
  return isDiatonic(chord, parallelKey(key));
@@ -542,9 +565,7 @@ function borrowedSource(chord, key) {
542
565
  if (isDiatonic(chord, key)) {
543
566
  return null;
544
567
  }
545
- if (isNeapolitan(chord, key)) {
546
- return "neapolitan";
547
- }
568
+ if (isNeapolitan(chord, key)) return "neapolitan";
548
569
  if (isBorrowedChord(chord, key)) {
549
570
  return isMinorKey(key) ? "parallelMajor" : "parallelMinor";
550
571
  }
@@ -578,13 +599,27 @@ var FUNCTION_BY_OFFSET = [
578
599
  "dominant"
579
600
  // 11 vii
580
601
  ];
602
+ var MINOR_FUNCTION_BY_OFFSET = [
603
+ "tonic",
604
+ "subdominant",
605
+ "subdominant",
606
+ "tonic",
607
+ "dominant",
608
+ "subdominant",
609
+ "dominant",
610
+ "dominant",
611
+ "tonic",
612
+ "tonic",
613
+ "subdominant",
614
+ "dominant"
615
+ ];
581
616
  function isMinorKey(key) {
582
617
  const hasMinorThird = key.modeMask12 >> 3 & 1;
583
618
  const hasMajorThird2 = key.modeMask12 >> 4 & 1;
584
619
  return Boolean(hasMinorThird) && !hasMajorThird2;
585
620
  }
586
621
  function functionOf(chord, key) {
587
- const offset = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc);
622
+ const offset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc);
588
623
  if (isNeapolitan(chord, key)) {
589
624
  return "subdominant";
590
625
  }
@@ -594,65 +629,75 @@ function functionOf(chord, key) {
594
629
  if (!isMinorKey(key) && hasMajorThird(chord) && (offset === 8 || offset === 10)) {
595
630
  return "subdominant";
596
631
  }
597
- return _nullishCoalesce(FUNCTION_BY_OFFSET[offset], () => ( "tonic"));
632
+ const functions = isMinorKey(key) ? MINOR_FUNCTION_BY_OFFSET : FUNCTION_BY_OFFSET;
633
+ return _nullishCoalesce(functions[offset], () => ( "tonic"));
598
634
  }
599
635
  function isAppliedDominant(chord, key) {
600
636
  if (isDiatonic(chord, key)) {
601
637
  return false;
602
638
  }
603
- const root = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
604
- const resolvesTo = (step) => _chunkFNDWXRRIcjs.isScaleTone.call(void 0, _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, root + step), key);
605
- if (hasMajorThird(chord) && chord.intervals.some((i) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, i) === 10)) {
606
- return resolvesTo(5) || resolvesTo(11);
639
+ const root = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc);
640
+ const resolvesTo = (step) => _chunkBSLTLGM3cjs.isScaleTone.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, root + step), key);
641
+ if (isAppliedDominantSonority(chord)) {
642
+ return chord.quality === "maj" ? resolvesTo(5) : resolvesTo(5) || resolvesTo(11);
607
643
  }
608
644
  return isDiminishedQuality(chord.quality) && resolvesTo(1);
609
645
  }
610
646
  function isDiatonic(chord, key) {
611
- return _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunkFNDWXRRIcjs.isScaleTone.call(void 0, pc, key));
647
+ return _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord).every((pc) => _chunkBSLTLGM3cjs.isScaleTone.call(void 0, pc, key));
612
648
  }
613
649
  function parallelKey(key) {
614
650
  return {
615
- rootPc: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc),
616
- modeMask12: isMinorKey(key) ? _chunkFNDWXRRIcjs.MAJOR_MASK : _chunkFNDWXRRIcjs.NATURAL_MINOR_MASK
651
+ rootPc: _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc),
652
+ modeMask12: isMinorKey(key) ? _chunkBSLTLGM3cjs.MAJOR_MASK : _chunkBSLTLGM3cjs.NATURAL_MINOR_MASK
617
653
  };
618
654
  }
619
655
  function isDiminishedQuality(quality) {
620
656
  return quality === "dim" || quality === "dim7" || quality === "m7b5";
621
657
  }
622
658
  function hasMajorThird(chord) {
623
- return chord.intervals.some((interval) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, interval) === 4);
659
+ return chord.intervals.some((interval) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, interval) === 4);
624
660
  }
625
- function analyzeChord(chord, key) {
661
+ function analyzeChord(chord, key, opts = {}) {
626
662
  const source = borrowedSource(chord, key);
627
663
  return {
628
664
  function: functionOf(chord, key),
629
665
  borrowed: source !== null,
630
666
  source,
631
- roman: chordToRoman(chord, key)
667
+ roman: chordToRoman(chord, key, opts)
632
668
  };
633
669
  }
634
670
  function secondaryDominant(targetDegree, key) {
635
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, targetDegree, "targetDegree", 0, 6);
671
+ const degreeCount = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).length;
672
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, targetDegree, "targetDegree", 0, degreeCount - 1);
636
673
  const targetRoot = degreeRootPc(targetDegree + 1, key);
637
- return _chunkFNDWXRRIcjs.makeChord.call(void 0, _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, targetRoot + 7), "dom7");
674
+ return _chunkBSLTLGM3cjs.makeChord.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, targetRoot + 7), "dom7");
638
675
  }
639
676
 
640
677
  // src/analyze/functional/cadence.ts
678
+ function hasDominantThird(chord) {
679
+ return chord.intervals.some((interval) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, interval) === 4);
680
+ }
681
+ function isLeadingToneDiminished(chord) {
682
+ return chord.quality === "dim" || chord.quality === "dim7" || chord.quality === "m7b5";
683
+ }
641
684
  function detectCadence(from, to, key) {
642
- const tonic = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
643
- const fromOffset = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, from.rootPc - tonic);
644
- const toOffset = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, to.rootPc - tonic);
685
+ const tonic = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
686
+ const fromOffset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, from.rootPc - tonic);
687
+ const toOffset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, to.rootPc - tonic);
645
688
  const deceptiveTargets = isMinorKey(key) ? [8] : [8, 9];
646
- if (fromOffset === 7 && toOffset === 0) {
689
+ const dominant = fromOffset === 7 && hasDominantThird(from);
690
+ const leadingTone = fromOffset === 11 && isLeadingToneDiminished(from);
691
+ if ((dominant || leadingTone) && toOffset === 0) {
647
692
  return "authentic";
648
693
  }
649
694
  if (fromOffset === 5 && toOffset === 0) {
650
695
  return "plagal";
651
696
  }
652
- if (fromOffset === 7 && deceptiveTargets.includes(toOffset)) {
697
+ if (dominant && deceptiveTargets.includes(toOffset)) {
653
698
  return "deceptive";
654
699
  }
655
- if (toOffset === 7 && fromOffset !== 7) {
700
+ if (toOffset === 7 && hasDominantThird(to) && fromOffset !== 7) {
656
701
  return "half";
657
702
  }
658
703
  return null;
@@ -676,4 +721,4 @@ function detectCadence(from, to, key) {
676
721
 
677
722
 
678
723
  exports.detectChord = detectChord; exports.detectChordBest = detectChordBest; exports.detectKey = detectKey; exports.detectKeyFromNotes = detectKeyFromNotes; exports.detectKeyBest = detectKeyBest; exports.romanToChord = romanToChord; exports.chordToRoman = chordToRoman; exports.isMinorKey = isMinorKey; exports.functionOf = functionOf; exports.isDiatonic = isDiatonic; exports.parallelKey = parallelKey; exports.analyzeChord = analyzeChord; exports.secondaryDominant = secondaryDominant; exports.isBorrowedChord = isBorrowedChord; exports.borrowedSource = borrowedSource; exports.detectCadence = detectCadence;
679
- //# sourceMappingURL=chunk-CAN76NCU.cjs.map
724
+ //# sourceMappingURL=chunk-KQN2ZYND.cjs.map