@libraz/libcantus 0.9.3 → 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 (201) hide show
  1. package/README.md +29 -20
  2. package/README_ja.md +268 -0
  3. package/dist/analyze/index.cjs +39 -1947
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +161 -91
  6. package/dist/analyze/index.d.ts +161 -91
  7. package/dist/analyze/index.js +19 -8
  8. package/dist/chunk-2A2QMA74.cjs +261 -0
  9. package/dist/chunk-2A2QMA74.cjs.map +1 -0
  10. package/dist/chunk-2PTFGVD7.js +261 -0
  11. package/dist/chunk-2PTFGVD7.js.map +1 -0
  12. package/dist/chunk-357ZDPVU.cjs +162 -0
  13. package/dist/chunk-357ZDPVU.cjs.map +1 -0
  14. package/dist/chunk-3KX76RLG.js +1548 -0
  15. package/dist/chunk-3KX76RLG.js.map +1 -0
  16. package/dist/chunk-4VZ5MZAM.cjs +889 -0
  17. package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
  18. package/dist/{chunk-C5XYKUVG.js → chunk-7EONYVI5.js} +471 -171
  19. package/dist/chunk-7EONYVI5.js.map +1 -0
  20. package/dist/{chunk-IT2O4XFE.js → chunk-7JEP3MFY.js} +42 -34
  21. package/dist/chunk-7JEP3MFY.js.map +1 -0
  22. package/dist/{chunk-4MHA432G.js → chunk-AID75RN3.js} +572 -250
  23. package/dist/chunk-AID75RN3.js.map +1 -0
  24. package/dist/chunk-BSLTLGM3.cjs +497 -0
  25. package/dist/chunk-BSLTLGM3.cjs.map +1 -0
  26. package/dist/chunk-CU6VG2YM.cjs +109 -0
  27. package/dist/chunk-CU6VG2YM.cjs.map +1 -0
  28. package/dist/{chunk-EH6EM37U.js → chunk-DZMHIGI4.js} +81 -59
  29. package/dist/chunk-DZMHIGI4.js.map +1 -0
  30. package/dist/{chunk-AKLYPWIN.js → chunk-EDOTEABK.js} +45 -19
  31. package/dist/chunk-EDOTEABK.js.map +1 -0
  32. package/dist/{chunk-2ZWMSJH6.js → chunk-HN2OEUN2.js} +7 -4
  33. package/dist/chunk-HN2OEUN2.js.map +1 -0
  34. package/dist/{chunk-MIWO425M.js → chunk-KBTTEX7Q.js} +128 -43
  35. package/dist/chunk-KBTTEX7Q.js.map +1 -0
  36. package/dist/chunk-KQN2ZYND.cjs +724 -0
  37. package/dist/chunk-KQN2ZYND.cjs.map +1 -0
  38. package/dist/chunk-MPTVJIVG.cjs +3176 -0
  39. package/dist/chunk-MPTVJIVG.cjs.map +1 -0
  40. package/dist/chunk-MTMZTMWM.cjs +53 -0
  41. package/dist/chunk-MTMZTMWM.cjs.map +1 -0
  42. package/dist/{chunk-CHMXELDS.js → chunk-NNB67XA2.js} +372 -197
  43. package/dist/chunk-NNB67XA2.js.map +1 -0
  44. package/dist/chunk-OL3IHHSJ.cjs +1204 -0
  45. package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
  46. package/dist/chunk-PI3UXRWK.cjs +528 -0
  47. package/dist/chunk-PI3UXRWK.cjs.map +1 -0
  48. package/dist/chunk-R2VVFAIN.js +528 -0
  49. package/dist/chunk-R2VVFAIN.js.map +1 -0
  50. package/dist/chunk-T64C7ZD7.cjs +1 -0
  51. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  52. package/dist/chunk-WFDXOLUB.cjs +337 -0
  53. package/dist/chunk-WFDXOLUB.cjs.map +1 -0
  54. package/dist/chunk-WTAN4ACG.cjs +1548 -0
  55. package/dist/chunk-WTAN4ACG.cjs.map +1 -0
  56. package/dist/chunk-YFJXS3AU.cjs +33 -0
  57. package/dist/chunk-YFJXS3AU.cjs.map +1 -0
  58. package/dist/{chunk-GOAW4VYL.js → chunk-YUE6TJ4M.js} +315 -145
  59. package/dist/chunk-YUE6TJ4M.js.map +1 -0
  60. package/dist/{chunk-62QZKCJQ.js → chunk-ZGLJM4A4.js} +10 -6
  61. package/dist/chunk-ZGLJM4A4.js.map +1 -0
  62. package/dist/core/index.cjs +140 -647
  63. package/dist/core/index.cjs.map +1 -1
  64. package/dist/core/index.d.cts +313 -41
  65. package/dist/core/index.d.ts +313 -41
  66. package/dist/core/index.js +64 -16
  67. package/dist/{function-DVH4Eusr.d.ts → function-CxixTB02.d.cts} +122 -10
  68. package/dist/{function-DYt4iKsn.d.cts → function-DTCt675z.d.ts} +122 -10
  69. package/dist/generate/index.cjs +28 -4085
  70. package/dist/generate/index.cjs.map +1 -1
  71. package/dist/generate/index.d.cts +374 -66
  72. package/dist/generate/index.d.ts +374 -66
  73. package/dist/generate/index.js +23 -9
  74. package/dist/{index-Jc0Ryi79.d.ts → index-B2Ulxxpo.d.cts} +21 -5
  75. package/dist/{index-CpOsP1y9.d.cts → index-BkYJ0xMq.d.cts} +18 -17
  76. package/dist/{index-CxqBQtH1.d.cts → index-BuaB-ypt.d.ts} +21 -5
  77. package/dist/index-CAN7v-kw.d.ts +199 -0
  78. package/dist/{index-Bt4tOUD9.d.ts → index-COw1Ky9g.d.ts} +93 -8
  79. package/dist/{index-Dikst44v.d.cts → index-Cfx8xgH0.d.ts} +14 -11
  80. package/dist/{index-DxvARIP2.d.ts → index-DRtIhlJj.d.ts} +18 -17
  81. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  82. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  83. package/dist/index-alrRninU.d.cts +199 -0
  84. package/dist/{index-BNlGP86A.d.cts → index-oTQbYYIs.d.cts} +93 -8
  85. package/dist/index-tR9jTn26.d.cts +275 -0
  86. package/dist/index-tR9jTn26.d.ts +275 -0
  87. package/dist/{index-Bg5dYMmZ.d.ts → index-x2vcsduE.d.cts} +14 -11
  88. package/dist/index.cjs +429 -7383
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +15 -15
  91. package/dist/index.d.ts +15 -15
  92. package/dist/index.js +121 -27
  93. package/dist/model/index.cjs +8 -2562
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +471 -81
  96. package/dist/model/index.d.ts +471 -81
  97. package/dist/model/index.js +6 -7
  98. package/dist/styled-Bl0ZK28h.d.ts +543 -0
  99. package/dist/styled-Crv6SA4S.d.cts +543 -0
  100. package/dist/theory/index.cjs +172 -1822
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +151 -233
  103. package/dist/theory/index.d.ts +151 -233
  104. package/dist/theory/index.js +30 -7
  105. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  106. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  107. package/package.json +34 -13
  108. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  109. package/dist/chunk-4MHA432G.js.map +0 -1
  110. package/dist/chunk-62QZKCJQ.js.map +0 -1
  111. package/dist/chunk-AKLYPWIN.js.map +0 -1
  112. package/dist/chunk-C5XYKUVG.js.map +0 -1
  113. package/dist/chunk-CHMXELDS.js.map +0 -1
  114. package/dist/chunk-DYWFNC4A.js +0 -152
  115. package/dist/chunk-DYWFNC4A.js.map +0 -1
  116. package/dist/chunk-EH6EM37U.js.map +0 -1
  117. package/dist/chunk-FBQK3ZQN.js +0 -131
  118. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  119. package/dist/chunk-GOAW4VYL.js.map +0 -1
  120. package/dist/chunk-I5R7ZBJS.js +0 -202
  121. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  122. package/dist/chunk-IT2O4XFE.js.map +0 -1
  123. package/dist/chunk-MIWO425M.js.map +0 -1
  124. package/dist/chunk-SGU7SPLK.js +0 -924
  125. package/dist/chunk-SGU7SPLK.js.map +0 -1
  126. package/dist/index-CGMZNCx6.d.cts +0 -140
  127. package/dist/index-CGMZNCx6.d.ts +0 -140
  128. package/dist/index-CnZCo29u.d.cts +0 -108
  129. package/dist/index-JnF4v9bx.d.ts +0 -108
  130. package/dist/styled-DZ7GjShb.d.ts +0 -282
  131. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  132. package/src/analyze/arrange/index.ts +0 -21
  133. package/src/analyze/arrange/internal.ts +0 -127
  134. package/src/analyze/arrange/tension.ts +0 -199
  135. package/src/analyze/arrange/tracks.ts +0 -312
  136. package/src/analyze/detect/index.ts +0 -272
  137. package/src/analyze/functional/borrowed.ts +0 -84
  138. package/src/analyze/functional/cadence.ts +0 -66
  139. package/src/analyze/functional/function.ts +0 -184
  140. package/src/analyze/functional/index.ts +0 -23
  141. package/src/analyze/functional/internal.ts +0 -29
  142. package/src/analyze/functional/roman.ts +0 -402
  143. package/src/analyze/index.ts +0 -49
  144. package/src/analyze/timeline/index.ts +0 -451
  145. package/src/analyze/voice/index.ts +0 -339
  146. package/src/core/event-index/index.ts +0 -104
  147. package/src/core/index.ts +0 -61
  148. package/src/core/interval/index.ts +0 -81
  149. package/src/core/meter/index.ts +0 -302
  150. package/src/core/pitch/index.ts +0 -311
  151. package/src/core/random/index.ts +0 -76
  152. package/src/core/tuning/index.ts +0 -186
  153. package/src/core/types.ts +0 -29
  154. package/src/core/validation/index.ts +0 -119
  155. package/src/generate/bass/index.ts +0 -373
  156. package/src/generate/countermelody/index.ts +0 -470
  157. package/src/generate/drums/beat.ts +0 -266
  158. package/src/generate/drums/euclid.ts +0 -65
  159. package/src/generate/drums/fills.ts +0 -356
  160. package/src/generate/drums/ghost.ts +0 -119
  161. package/src/generate/drums/hihat.ts +0 -285
  162. package/src/generate/drums/hit.ts +0 -31
  163. package/src/generate/drums/index.ts +0 -361
  164. package/src/generate/drums/internal.ts +0 -257
  165. package/src/generate/drums/kick.ts +0 -160
  166. package/src/generate/drums/percussion.ts +0 -121
  167. package/src/generate/drums/rng.ts +0 -7
  168. package/src/generate/drums/swing.ts +0 -71
  169. package/src/generate/groove/index.ts +0 -351
  170. package/src/generate/harmonize/index.ts +0 -451
  171. package/src/generate/index.ts +0 -58
  172. package/src/generate/motif/index.ts +0 -422
  173. package/src/generate/progression/index.ts +0 -362
  174. package/src/generate/reharmony/index.ts +0 -323
  175. package/src/generate/rhythm/index.ts +0 -175
  176. package/src/index.ts +0 -13
  177. package/src/model/chord.ts +0 -442
  178. package/src/model/index.ts +0 -15
  179. package/src/model/interval.ts +0 -82
  180. package/src/model/key.ts +0 -266
  181. package/src/model/note.ts +0 -176
  182. package/src/model/progression.ts +0 -161
  183. package/src/model/shared.ts +0 -12
  184. package/src/theory/chord/index.ts +0 -375
  185. package/src/theory/chordscale/index.ts +0 -374
  186. package/src/theory/counterpoint/index.ts +0 -230
  187. package/src/theory/harmony/index.ts +0 -77
  188. package/src/theory/index.ts +0 -112
  189. package/src/theory/safety/index.ts +0 -488
  190. package/src/theory/scale/degrees.ts +0 -123
  191. package/src/theory/scale/index.ts +0 -28
  192. package/src/theory/scale/key.ts +0 -56
  193. package/src/theory/scale/masks.ts +0 -153
  194. package/src/theory/spelling/index.ts +0 -249
  195. package/src/theory/symbol/index.ts +0 -268
  196. package/src/theory/voicing/index.ts +0 -5
  197. package/src/theory/voicing/internal.ts +0 -168
  198. package/src/theory/voicing/leading.ts +0 -112
  199. package/src/theory/voicing/satb.ts +0 -216
  200. package/src/theory/voicing/styled.ts +0 -199
  201. package/tsconfig.json +0 -23
@@ -0,0 +1,162 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+
8
+ var _chunkKQN2ZYNDcjs = require('./chunk-KQN2ZYND.cjs');
9
+
10
+
11
+
12
+
13
+
14
+ var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
15
+
16
+
17
+ var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
18
+
19
+ // src/generate/reharmony/index.ts
20
+ function hasInterval(chord, semitone) {
21
+ return chord.intervals.some((iv) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, iv) === semitone);
22
+ }
23
+ function isDominantType(chord) {
24
+ return hasInterval(chord, 4) && hasInterval(chord, 10);
25
+ }
26
+ function commonToneCount(a, b) {
27
+ const set = new Set(b);
28
+ return a.filter((pc) => set.has(pc)).length;
29
+ }
30
+ function triadQualityOf(chord) {
31
+ const semis = new Set(chord.intervals.map((interval) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, interval)));
32
+ const hasThird = semis.has(4) ? "maj" : semis.has(3) ? "min" : void 0;
33
+ if (hasThird === void 0) {
34
+ return chord.quality;
35
+ }
36
+ if (semis.has(6) && !semis.has(7)) {
37
+ return hasThird === "min" ? "dim" : "majb5";
38
+ }
39
+ if (semis.has(8) && !semis.has(7)) {
40
+ return "aug";
41
+ }
42
+ return hasThird;
43
+ }
44
+ function diatonicTriadsOf(key) {
45
+ const degrees = _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, key).length;
46
+ const triads = [];
47
+ for (let degree = 0; degree < degrees; degree += 1) {
48
+ triads.push(_chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, key));
49
+ }
50
+ return triads;
51
+ }
52
+ function sameChord(a, b) {
53
+ return _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, a.rootPc) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, b.rootPc) && a.quality === b.quality;
54
+ }
55
+ var THIRD_OFFSETS = [3, 4, 8, 9];
56
+ function substituteChord(chord, key, opts) {
57
+ const originalTriad = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, _chunkBSLTLGM3cjs.makeChord.call(void 0, chord.rootPc, triadQualityOf(chord)));
58
+ const candidates = [];
59
+ if (isDominantType(chord)) {
60
+ candidates.push({ chord: _chunkBSLTLGM3cjs.makeChord.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + 6), "dom7"), type: "tritone" });
61
+ }
62
+ for (const triad of diatonicTriadsOf(key)) {
63
+ const offset = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, triad.rootPc - chord.rootPc);
64
+ if (THIRD_OFFSETS.includes(offset) && commonToneCount(originalTriad, _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, triad)) === 2) {
65
+ candidates.push({ chord: triad, type: "relative" });
66
+ }
67
+ }
68
+ const targetFunction = _chunkKQN2ZYNDcjs.functionOf.call(void 0, chord, key);
69
+ for (const triad of diatonicTriadsOf(_chunkKQN2ZYNDcjs.parallelKey.call(void 0, key))) {
70
+ if (_chunkKQN2ZYNDcjs.functionOf.call(void 0, triad, key) === targetFunction && !_chunkKQN2ZYNDcjs.isDiatonic.call(void 0, triad, key)) {
71
+ candidates.push({ chord: triad, type: "borrowed" });
72
+ }
73
+ }
74
+ for (const offset of THIRD_OFFSETS) {
75
+ for (const quality of ["maj", "min"]) {
76
+ const mediant = _chunkBSLTLGM3cjs.makeChord.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + offset), quality);
77
+ if (commonToneCount(originalTriad, _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, mediant)) === 1 && !_chunkKQN2ZYNDcjs.isDiatonic.call(void 0, mediant, key)) {
78
+ candidates.push({ chord: mediant, type: "chromaticMediant" });
79
+ }
80
+ }
81
+ }
82
+ const melodyPcs = _optionalChain([opts, 'optionalAccess', _ => _.melodyPcs, 'optionalAccess', _2 => _2.map, 'call', _3 => _3(_chunkPI3UXRWKcjs.pitchClassOf)]);
83
+ const seen = /* @__PURE__ */ new Set();
84
+ const results = [];
85
+ for (const candidate of candidates) {
86
+ if (sameChord(candidate.chord, chord)) {
87
+ continue;
88
+ }
89
+ const dedupeKey = `${_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, candidate.chord.rootPc)}:${candidate.chord.quality}`;
90
+ if (seen.has(dedupeKey)) {
91
+ continue;
92
+ }
93
+ seen.add(dedupeKey);
94
+ if (melodyPcs !== void 0) {
95
+ const pcs = new Set(_chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, candidate.chord));
96
+ if (!melodyPcs.every((pc) => pcs.has(pc))) {
97
+ continue;
98
+ }
99
+ }
100
+ results.push({
101
+ chord: candidate.chord,
102
+ type: candidate.type,
103
+ // A secondary dominant is named as the applied chord it is, so the
104
+ // numeral re-parses to the chord that was proposed rather than to a
105
+ // chromatic chord on the same root.
106
+ roman: _chunkKQN2ZYNDcjs.chordToRoman.call(void 0, candidate.chord, key, {
107
+ applied: candidate.type === "secondaryDominant"
108
+ }),
109
+ function: _chunkKQN2ZYNDcjs.functionOf.call(void 0, candidate.chord, key)
110
+ });
111
+ }
112
+ return results;
113
+ }
114
+ function modalInterchangePalette(key) {
115
+ const palette = [];
116
+ const seen = /* @__PURE__ */ new Set();
117
+ const add = (chord) => {
118
+ const dedupeKey = `${_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc)}:${chord.quality}`;
119
+ if (seen.has(dedupeKey)) {
120
+ return;
121
+ }
122
+ seen.add(dedupeKey);
123
+ palette.push({
124
+ chord,
125
+ roman: _chunkKQN2ZYNDcjs.chordToRoman.call(void 0, chord, key),
126
+ source: _chunkKQN2ZYNDcjs.borrowedSource.call(void 0, chord, key)
127
+ });
128
+ };
129
+ for (const triad of diatonicTriadsOf(_chunkKQN2ZYNDcjs.parallelKey.call(void 0, key))) {
130
+ if (!_chunkKQN2ZYNDcjs.isDiatonic.call(void 0, triad, key) && _chunkKQN2ZYNDcjs.borrowedSource.call(void 0, triad, key) !== null) {
131
+ add(triad);
132
+ }
133
+ }
134
+ const neapolitan = _chunkBSLTLGM3cjs.makeChord.call(void 0, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc + 1), "maj");
135
+ if (!_chunkKQN2ZYNDcjs.isDiatonic.call(void 0, neapolitan, key)) {
136
+ add(neapolitan);
137
+ }
138
+ return palette;
139
+ }
140
+ function negativeHarmonyMirror(chord, key) {
141
+ const tonic = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, key.rootPc);
142
+ const mirror = (p) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, 2 * tonic + 7 - p);
143
+ const pcs = _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, chord).map(mirror);
144
+ const bassPc = chord.bassPc !== void 0 ? mirror(chord.bassPc) : void 0;
145
+ const pitches = bassPc !== void 0 ? [bassPc - 12, ...pcs] : pcs;
146
+ const detected = _chunkKQN2ZYNDcjs.detectChordBest.call(void 0, pitches);
147
+ if (detected) {
148
+ if (bassPc === void 0 && detected.bassPc !== void 0) {
149
+ return _chunkBSLTLGM3cjs.makeChord.call(void 0, detected.rootPc, detected.quality);
150
+ }
151
+ return detected;
152
+ }
153
+ const quality = chord.quality;
154
+ return _chunkBSLTLGM3cjs.makeChord.call(void 0, mirror(chord.rootPc), quality, bassPc);
155
+ }
156
+
157
+
158
+
159
+
160
+
161
+ exports.substituteChord = substituteChord; exports.modalInterchangePalette = modalInterchangePalette; exports.negativeHarmonyMirror = negativeHarmonyMirror;
162
+ //# sourceMappingURL=chunk-357ZDPVU.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/libcantus/libcantus/dist/chunk-357ZDPVU.cjs","../src/generate/reharmony/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/libcantus/libcantus/dist/chunk-357ZDPVU.cjs","sourcesContent":[null,"/**\n * Reharmonization: chord substitution, modal-interchange palettes, and negative\n * harmony. These transforms take an existing chord (or a key) and propose\n * alternative harmonies that preserve a chosen relationship — a shared function,\n * a common tone, a tonicizing dominant, or a reflection across the key axis.\n *\n * Roots are pitch classes (0..11); no spelled key signature is required.\n */\n\nimport { detectChordBest } from '../../analyze/detect/index.js';\nimport {\n type BorrowedSource,\n borrowedSource,\n chordToRoman,\n functionOf,\n type HarmonicFunction,\n isDiatonic,\n parallelKey,\n} from '../../analyze/functional/index.js';\nimport { pitchClassOf as mod12 } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport {\n type Chord,\n type ChordQuality,\n chordPitchClasses,\n diatonicTriad,\n makeChord,\n} from '../../theory/chord/index.js';\nimport { scaleTonesInDegreeOrder } from '../../theory/scale/index.js';\n\n/**\n * The kind of substitution relationship a candidate realizes.\n *\n * @category Reharmonization\n */\nexport type SubstitutionType =\n | 'tritone'\n | 'relative'\n | 'borrowed'\n | 'chromaticMediant'\n | 'secondaryDominant';\n\n/**\n * A proposed chord substitution with its relationship, numeral, and function.\n *\n * @category Reharmonization\n */\nexport type Substitution = {\n chord: Chord;\n type: SubstitutionType;\n roman: string;\n function: HarmonicFunction;\n};\n\n/**\n * Options controlling which substitutions {@link substituteChord} returns.\n *\n * @category Reharmonization\n */\nexport type SubstituteOptions = {\n /**\n * Melody pitch classes that must remain chord tones. When given, only\n * substitutions whose pitch classes contain every listed pitch class are\n * kept, so the melody stays consonant against the new harmony.\n */\n melodyPcs?: number[];\n};\n\n/** Whether the chord's template carries a given interval (mod 12). */\nfunction hasInterval(chord: Chord, semitone: number): boolean {\n return chord.intervals.some((iv) => mod12(iv) === semitone);\n}\n\n/** Whether a chord is a dominant type: a major third with a minor seventh. */\nfunction isDominantType(chord: Chord): boolean {\n return hasInterval(chord, 4) && hasInterval(chord, 10);\n}\n\n/** Count the pitch classes shared between two pitch-class sets. */\nfunction commonToneCount(a: number[], b: number[]): number {\n const set = new Set(b);\n return a.filter((pc) => set.has(pc)).length;\n}\n\n/** The plain triad quality underlying a chord's own quality. */\nfunction triadQualityOf(chord: Chord): ChordQuality {\n const semis = new Set(chord.intervals.map((interval) => mod12(interval)));\n const hasThird = semis.has(4) ? 'maj' : semis.has(3) ? 'min' : undefined;\n if (hasThird === undefined) {\n return chord.quality; // sus, power, and other non-tertian chords stand alone\n }\n if (semis.has(6) && !semis.has(7)) {\n return hasThird === 'min' ? 'dim' : 'majb5';\n }\n if (semis.has(8) && !semis.has(7)) {\n return 'aug';\n }\n return hasThird;\n}\n\n/** The diatonic triads of a key, one per scale degree. */\nfunction diatonicTriadsOf(key: KeyScale): Chord[] {\n const degrees = scaleTonesInDegreeOrder(key).length;\n const triads: Chord[] = [];\n for (let degree = 0; degree < degrees; degree += 1) {\n triads.push(diatonicTriad(degree, key));\n }\n return triads;\n}\n\n/** Whether two chords share the same root pitch class and quality. */\nfunction sameChord(a: Chord, b: Chord): boolean {\n return mod12(a.rootPc) === mod12(b.rootPc) && a.quality === b.quality;\n}\n\n/** Root offsets (semitones) that form a third above or below a root. */\nconst THIRD_OFFSETS = [3, 4, 8, 9] as const;\n\n/**\n * Propose substitutions for a chord within a key.\n *\n * Candidates are generated by several relationships and deduplicated against the\n * original chord and each other:\n *\n * - `tritone`: for a dominant-type chord, the dominant seventh a tritone away.\n * - `relative`: a diatonic triad a third away that shares two common tones.\n * - `borrowed`: a triad from the parallel mode with the same harmonic function.\n * - `chromaticMediant`: a major/minor triad a third away sharing one common\n * tone.\n *\n * Each result carries its Roman numeral and harmonic function in `key`. When\n * `opts.melodyPcs` is given, only substitutions whose pitch classes contain all\n * of those pitch classes are returned.\n *\n * @param chord The chord to reharmonize.\n * @param key The prevailing key.\n * @param opts Optional melody-preservation constraint.\n * @returns The deduplicated substitution candidates.\n * @example\n * ```ts\n * import { substituteChord, makeChord, majorKey } from '@libraz/libcantus';\n * const subs = substituteChord(makeChord(7, 'dom7'), majorKey(0));\n * // includes the tritone sub Db7 (rootPc 1, quality 'dom7')\n * ```\n * @category Reharmonization\n */\nexport function substituteChord(\n chord: Chord,\n key: KeyScale,\n opts?: SubstituteOptions,\n): Substitution[] {\n // The relative test counts common tones against the chord's triad, not its\n // full pitch-class set: an exact count of two would otherwise be decided by\n // how many tensions the input carries, so C proposes both Em and Am while\n // Cmaj7 proposes only Am — penalising the candidate that overlaps *more*.\n const originalTriad = chordPitchClasses(makeChord(chord.rootPc, triadQualityOf(chord)));\n const candidates: { chord: Chord; type: SubstitutionType }[] = [];\n\n // Tritone substitution: only for dominant-type chords.\n if (isDominantType(chord)) {\n candidates.push({ chord: makeChord(mod12(chord.rootPc + 6), 'dom7'), type: 'tritone' });\n }\n\n // Relative: a diatonic triad a third away sharing two common tones.\n for (const triad of diatonicTriadsOf(key)) {\n const offset = mod12(triad.rootPc - chord.rootPc);\n if (\n (THIRD_OFFSETS as readonly number[]).includes(offset) &&\n commonToneCount(originalTriad, chordPitchClasses(triad)) === 2\n ) {\n candidates.push({ chord: triad, type: 'relative' });\n }\n }\n\n // Borrowed: a parallel-mode triad sharing the original's harmonic function.\n const targetFunction = functionOf(chord, key);\n for (const triad of diatonicTriadsOf(parallelKey(key))) {\n if (functionOf(triad, key) === targetFunction && !isDiatonic(triad, key)) {\n candidates.push({ chord: triad, type: 'borrowed' });\n }\n }\n\n // Chromatic mediants: major/minor triads a third away sharing one common tone.\n for (const offset of THIRD_OFFSETS) {\n for (const quality of ['maj', 'min'] as const) {\n const mediant = makeChord(mod12(chord.rootPc + offset), quality);\n if (\n commonToneCount(originalTriad, chordPitchClasses(mediant)) === 1 &&\n !isDiatonic(mediant, key)\n ) {\n candidates.push({ chord: mediant, type: 'chromaticMediant' });\n }\n }\n }\n\n const melodyPcs = opts?.melodyPcs?.map(mod12);\n const seen = new Set<string>();\n const results: Substitution[] = [];\n for (const candidate of candidates) {\n if (sameChord(candidate.chord, chord)) {\n continue;\n }\n const dedupeKey = `${mod12(candidate.chord.rootPc)}:${candidate.chord.quality}`;\n if (seen.has(dedupeKey)) {\n continue;\n }\n seen.add(dedupeKey);\n if (melodyPcs !== undefined) {\n const pcs = new Set(chordPitchClasses(candidate.chord));\n if (!melodyPcs.every((pc) => pcs.has(pc))) {\n continue;\n }\n }\n results.push({\n chord: candidate.chord,\n type: candidate.type,\n // A secondary dominant is named as the applied chord it is, so the\n // numeral re-parses to the chord that was proposed rather than to a\n // chromatic chord on the same root.\n roman: chordToRoman(candidate.chord, key, {\n applied: candidate.type === 'secondaryDominant',\n }),\n function: functionOf(candidate.chord, key),\n });\n }\n return results;\n}\n\n/**\n * A borrowed chord in the modal-interchange palette of a key.\n *\n * @category Reharmonization\n */\nexport type BorrowedChord = {\n chord: Chord;\n roman: string;\n source: BorrowedSource;\n};\n\n/**\n * The modal-interchange palette of a key.\n *\n * Lists the diatonic triads of the {@link parallelKey} that are not diatonic to\n * `key` (the classic borrowed chords such as `iv`, `bVI`, and `bVII` in a major\n * key), plus the Neapolitan (a major triad on the flat second degree). Each\n * carries its Roman numeral and {@link borrowedSource} relative to `key`.\n *\n * @param key The prevailing key.\n * @returns The borrowed-chord palette.\n * @category Reharmonization\n */\nexport function modalInterchangePalette(key: KeyScale): BorrowedChord[] {\n const palette: BorrowedChord[] = [];\n const seen = new Set<string>();\n const add = (chord: Chord) => {\n const dedupeKey = `${mod12(chord.rootPc)}:${chord.quality}`;\n if (seen.has(dedupeKey)) {\n return;\n }\n seen.add(dedupeKey);\n palette.push({\n chord,\n roman: chordToRoman(chord, key),\n source: borrowedSource(chord, key),\n });\n };\n\n for (const triad of diatonicTriadsOf(parallelKey(key))) {\n if (!isDiatonic(triad, key) && borrowedSource(triad, key) !== null) {\n add(triad);\n }\n }\n // The Neapolitan (major triad on b2) sits outside both parallel modes, so it\n // is added explicitly — unless b2 is already diatonic (e.g. in a Phrygian key,\n // where it is a native chord rather than a borrowing).\n const neapolitan = makeChord(mod12(key.rootPc + 1), 'maj');\n if (!isDiatonic(neapolitan, key)) {\n add(neapolitan);\n }\n return palette;\n}\n\n/**\n * Reflect a chord across the tonic–dominant axis of a key (negative harmony).\n *\n * Every pitch class `p` maps to `(2*T + 7 - p) mod 12`, where `T` is the key\n * tonic — the reflection that swaps the tonic with the dominant. The mirrored\n * pitch classes (including the bass, if set) are reassembled into a chord by\n * {@link detectChordBest}; when no chord is recognized, the mirrored root and\n * the original quality are used as a fallback.\n *\n * @param chord The chord to reflect.\n * @param key The prevailing key.\n * @returns The negative-harmony counterpart of the chord.\n * @example\n * ```ts\n * import { negativeHarmonyMirror, makeChord, majorKey } from '@libraz/libcantus';\n * negativeHarmonyMirror(makeChord(7, 'maj'), majorKey(0));\n * // G major reflected across C's axis => F minor (rootPc 5, quality 'min')\n * ```\n * @category Reharmonization\n */\nexport function negativeHarmonyMirror(chord: Chord, key: KeyScale): Chord {\n const tonic = mod12(key.rootPc);\n const mirror = (p: number) => mod12(2 * tonic + 7 - p);\n const pcs = chordPitchClasses(chord).map(mirror);\n const bassPc = chord.bassPc !== undefined ? mirror(chord.bassPc) : undefined;\n // Place the mirrored bass an octave below so it is recognized as the lowest\n // note and preserved through detection.\n const pitches = bassPc !== undefined ? [bassPc - 12, ...pcs] : pcs;\n const detected = detectChordBest(pitches);\n if (detected) {\n // Without an input bass the mirrored tones are octave-less, so detection may\n // read the lowest pitch class as a slash bass and report a spurious\n // inversion. Restore the recognized chord to root position in that case.\n if (bassPc === undefined && detected.bassPc !== undefined) {\n return makeChord(detected.rootPc, detected.quality);\n }\n return detected;\n }\n const quality: ChordQuality = chord.quality;\n return makeChord(mirror(chord.rootPc), quality, bassPc);\n}\n"]}