@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
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
2
 
3
- var _chunkZEJR4TKLcjs = require('./chunk-ZEJR4TKL.cjs');
3
+ var _chunk357ZDPVUcjs = require('./chunk-357ZDPVU.cjs');
4
4
 
5
5
 
6
6
 
@@ -14,7 +14,7 @@ var _chunkZEJR4TKLcjs = require('./chunk-ZEJR4TKL.cjs');
14
14
 
15
15
 
16
16
 
17
- var _chunkCAN76NCUcjs = require('./chunk-CAN76NCU.cjs');
17
+ var _chunkKQN2ZYNDcjs = require('./chunk-KQN2ZYND.cjs');
18
18
 
19
19
 
20
20
 
@@ -28,8 +28,8 @@ var _chunkCAN76NCUcjs = require('./chunk-CAN76NCU.cjs');
28
28
 
29
29
 
30
30
 
31
- var _chunkLW53AHQUcjs = require('./chunk-LW53AHQU.cjs');
32
31
 
32
+ var _chunkOL3IHHSJcjs = require('./chunk-OL3IHHSJ.cjs');
33
33
 
34
34
 
35
35
 
@@ -41,8 +41,8 @@ var _chunkLW53AHQUcjs = require('./chunk-LW53AHQU.cjs');
41
41
 
42
42
 
43
43
 
44
- var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
45
44
 
45
+ var _chunkBSLTLGM3cjs = require('./chunk-BSLTLGM3.cjs');
46
46
 
47
47
 
48
48
 
@@ -57,7 +57,9 @@ var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
57
57
 
58
58
 
59
59
 
60
- var _chunk7MGZ7D62cjs = require('./chunk-7MGZ7D62.cjs');
60
+
61
+
62
+ var _chunkPI3UXRWKcjs = require('./chunk-PI3UXRWK.cjs');
61
63
 
62
64
  // src/model/interval.ts
63
65
  var INVERSION_SUM = 9;
@@ -77,10 +79,12 @@ var Interval = class _Interval {
77
79
  #number;
78
80
  #quality;
79
81
  #semitones;
80
- constructor(numberValue, quality, semitones) {
82
+ #descending;
83
+ constructor(numberValue, quality, semitones, descending = semitones < 0) {
81
84
  this.#number = numberValue;
82
85
  this.#quality = quality;
83
86
  this.#semitones = semitones;
87
+ this.#descending = descending;
84
88
  }
85
89
  /**
86
90
  * The spelled interval between two notes.
@@ -104,9 +108,9 @@ var Interval = class _Interval {
104
108
  * `P5` spanning 8 semitones is not a value any other method can produce.
105
109
  */
106
110
  static of(numberValue, quality, semitones) {
107
- const expected = _chunk7MGZ7D62cjs.intervalSemitones.call(void 0, numberValue, quality);
111
+ const expected = _chunkPI3UXRWKcjs.intervalSemitones.call(void 0, numberValue, quality);
108
112
  if (Math.abs(semitones) !== expected) {
109
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
113
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
110
114
  `${quality}${numberValue} spans ${expected} semitones; received ${semitones}`
111
115
  );
112
116
  }
@@ -119,7 +123,12 @@ var Interval = class _Interval {
119
123
  * @returns The wrapped interval.
120
124
  */
121
125
  static fromData(data) {
122
- return new _Interval(data.number, data.quality, data.semitones);
126
+ return new _Interval(
127
+ data.number,
128
+ data.quality,
129
+ data.semitones,
130
+ _nullishCoalesce(data.descending, () => ( data.semitones < 0))
131
+ );
123
132
  }
124
133
  /**
125
134
  * Rebuild an interval from its {@link Interval.toJSON} output.
@@ -138,7 +147,7 @@ var Interval = class _Interval {
138
147
  * @throws If the name is not a quality label followed by a diatonic number.
139
148
  */
140
149
  static parse(name) {
141
- return _Interval.fromData(_chunk7MGZ7D62cjs.parseInterval.call(void 0, name));
150
+ return _Interval.fromData(_chunkPI3UXRWKcjs.parseInterval.call(void 0, name));
142
151
  }
143
152
  /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
144
153
  get number() {
@@ -166,10 +175,10 @@ var Interval = class _Interval {
166
175
  * @returns The inverted interval, e.g. `M3` becomes `m6`.
167
176
  */
168
177
  invert() {
169
- const simple = (this.#number - 1) % 7 + 1;
178
+ const simple = this.#number === 8 ? 8 : (this.#number - 1) % 7 + 1;
170
179
  const numberValue = INVERSION_SUM - simple;
171
180
  const quality = invertQuality(this.#quality);
172
- return _Interval.of(numberValue, quality, _chunk7MGZ7D62cjs.intervalSemitones.call(void 0, numberValue, quality));
181
+ return _Interval.of(numberValue, quality, _chunkPI3UXRWKcjs.intervalSemitones.call(void 0, numberValue, quality));
173
182
  }
174
183
  /**
175
184
  * Whether the interval is consonant.
@@ -179,7 +188,7 @@ var Interval = class _Interval {
179
188
  * @returns True if the interval is consonant in that context.
180
189
  */
181
190
  isConsonant(twoVoice = true) {
182
- return _chunk7MGZ7D62cjs.isConsonantInterval.call(void 0, this.#semitones, twoVoice);
191
+ return _chunkPI3UXRWKcjs.isConsonantInterval.call(void 0, this.#semitones, twoVoice);
183
192
  }
184
193
  /**
185
194
  * Whether another interval is spelled identically.
@@ -191,7 +200,7 @@ var Interval = class _Interval {
191
200
  * @returns True when number, quality, and span all match.
192
201
  */
193
202
  equals(other) {
194
- return this.#number === other.number && this.#quality === other.quality && this.#semitones === other.semitones;
203
+ return this.#number === other.number && this.#quality === other.quality && this.#semitones === other.semitones && this.#descending === other.#descending;
195
204
  }
196
205
  /**
197
206
  * The plain interval data, for JSON serialization.
@@ -202,7 +211,17 @@ var Interval = class _Interval {
202
211
  * @returns The diatonic number, quality, and semitone span.
203
212
  */
204
213
  toJSON() {
205
- return { number: this.#number, quality: this.#quality, semitones: this.#semitones };
214
+ return this.#descending ? {
215
+ number: this.#number,
216
+ quality: this.#quality,
217
+ semitones: this.#semitones,
218
+ descending: true
219
+ } : this.#semitones < 0 ? {
220
+ number: this.#number,
221
+ quality: this.#quality,
222
+ semitones: this.#semitones,
223
+ descending: false
224
+ } : { number: this.#number, quality: this.#quality, semitones: this.#semitones };
206
225
  }
207
226
  /**
208
227
  * The interval's name, so a template literal or a log line reads as the
@@ -218,7 +237,7 @@ var Interval = class _Interval {
218
237
  // src/model/note.ts
219
238
  function copyNote(data) {
220
239
  if (!Number.isInteger(data.letter) || data.letter < 0 || data.letter > 6) {
221
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
240
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
222
241
  `note letter must be an integer in [0, 6]; received ${data.letter}`
223
242
  );
224
243
  }
@@ -226,6 +245,7 @@ function copyNote(data) {
226
245
  if (data.octave !== void 0) {
227
246
  copy.octave = data.octave;
228
247
  }
248
+ _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, copy);
229
249
  return copy;
230
250
  }
231
251
  var Note = class _Note {
@@ -247,7 +267,7 @@ var Note = class _Note {
247
267
  * @throws If the text is not a valid note.
248
268
  */
249
269
  static of(name) {
250
- return new _Note(_chunk7MGZ7D62cjs.parseNote.call(void 0, name));
270
+ return new _Note(_chunkPI3UXRWKcjs.parseNote.call(void 0, name));
251
271
  }
252
272
  /**
253
273
  * Name a MIDI number (middle C = C4 = 60) as a note.
@@ -257,7 +277,7 @@ var Note = class _Note {
257
277
  * @returns The spelled note, with octave.
258
278
  */
259
279
  static fromMidi(midi, spelling = "sharp") {
260
- return new _Note(_chunk7MGZ7D62cjs.midiToNote.call(void 0, midi, spelling));
280
+ return new _Note(_chunkPI3UXRWKcjs.midiToNote.call(void 0, midi, spelling));
261
281
  }
262
282
  /**
263
283
  * Wrap an existing plain note object.
@@ -271,11 +291,11 @@ var Note = class _Note {
271
291
  }
272
292
  /** The note rendered as scientific pitch notation, e.g. `'G4'` or `'Bb'`. */
273
293
  get name() {
274
- return _chunk7MGZ7D62cjs.formatNote.call(void 0, this.#data);
294
+ return _chunkPI3UXRWKcjs.formatNote.call(void 0, this.#data);
275
295
  }
276
296
  /** The pitch class (0..11), ignoring octave. */
277
297
  get pitchClass() {
278
- return _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, this.#data);
298
+ return _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, this.#data);
279
299
  }
280
300
  /**
281
301
  * The MIDI number (middle C = C4 = 60).
@@ -284,9 +304,9 @@ var Note = class _Note {
284
304
  */
285
305
  get midi() {
286
306
  if (this.#data.octave === void 0) {
287
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`note ${this.name} has no octave, so it has no MIDI number`);
307
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(`note ${this.name} has no octave, so it has no MIDI number`);
288
308
  }
289
- return _chunk7MGZ7D62cjs.noteToMidi.call(void 0, this.#data);
309
+ return _chunkPI3UXRWKcjs.noteToMidi.call(void 0, this.#data);
290
310
  }
291
311
  /** The diatonic letter number: 0..6 for C..B. */
292
312
  get letter() {
@@ -328,7 +348,7 @@ var Note = class _Note {
328
348
  if (semitones === 0 && _optionalChain([opts, 'optionalAccess', _ => _.spelling]) === void 0) {
329
349
  return new _Note(this.#data);
330
350
  }
331
- return new _Note(_chunk7MGZ7D62cjs.transposeNote.call(void 0, this.#data, semitones, opts));
351
+ return new _Note(_chunkPI3UXRWKcjs.transposeNote.call(void 0, this.#data, semitones, opts));
332
352
  }
333
353
  /**
334
354
  * The spelled interval from this note to another.
@@ -337,7 +357,7 @@ var Note = class _Note {
337
357
  * @returns The interval, which {@link Interval.toJSON} unwraps to plain data.
338
358
  */
339
359
  intervalTo(other) {
340
- return Interval.fromData(_chunk7MGZ7D62cjs.spelledInterval.call(void 0, this.#data, other.data));
360
+ return Interval.fromData(_chunkPI3UXRWKcjs.spelledInterval.call(void 0, this.#data, other.data));
341
361
  }
342
362
  /**
343
363
  * Transpose by a spelled interval, keeping the spelling the interval names.
@@ -355,7 +375,7 @@ var Note = class _Note {
355
375
  * ```
356
376
  */
357
377
  transposeBy(interval) {
358
- return new _Note(_chunk7MGZ7D62cjs.transposeByInterval.call(void 0, this.#data, interval.toJSON()));
378
+ return new _Note(_chunkPI3UXRWKcjs.transposeByInterval.call(void 0, this.#data, interval.toJSON()));
359
379
  }
360
380
  /**
361
381
  * Rebuild a note from its {@link Note.toJSON} output.
@@ -400,13 +420,13 @@ var Note = class _Note {
400
420
 
401
421
  // src/model/shared.ts
402
422
  function spellPitchClassBare(pc, spelling) {
403
- const spelled = _chunk7MGZ7D62cjs.midiToNote.call(void 0, 60 + _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc), spelling);
423
+ const spelled = _chunkPI3UXRWKcjs.midiToNote.call(void 0, 60 + _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc), spelling);
404
424
  return { letter: spelled.letter, alter: spelled.alter };
405
425
  }
406
426
 
407
427
  // src/model/key.ts
408
428
  function accidentalLoad(tonic, scale) {
409
- return _chunkLW53AHQUcjs.spellScale.call(void 0, tonic, scale).reduce((sum, note) => sum + Math.abs(note.alter), 0);
429
+ return _chunkOL3IHHSJcjs.spellScale.call(void 0, tonic, scale).reduce((sum, note) => sum + Math.abs(note.alter), 0);
410
430
  }
411
431
  function bestTonicForScale(rootPc, scale) {
412
432
  const sharp = spellPitchClassBare(rootPc, "sharp");
@@ -419,21 +439,38 @@ function bestTonicForScale(rootPc, scale) {
419
439
  var Key = class _Key {
420
440
  #scale;
421
441
  #tonic;
442
+ #variant;
422
443
  /**
423
444
  * Wrap a key/scale and its spelled tonic.
424
445
  *
425
446
  * @param scale The key/scale; its root is normalized to a pitch class.
426
447
  * @param tonic The spelled tonic anchoring letter-name spelling.
448
+ * @param variant Optional detected scale form retained for display.
427
449
  */
428
- constructor(scale, tonic) {
429
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, scale.rootPc);
450
+ constructor(scale, tonic, variant) {
451
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, scale.rootPc, "scale.rootPc");
452
+ _chunkPI3UXRWKcjs.assertInteger.call(void 0, scale.modeMask12, "scale.modeMask12", 1, 4095);
453
+ if ((scale.modeMask12 & 1) === 0) {
454
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("scale.modeMask12 must include the tonic (bit 0)");
455
+ }
456
+ const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, scale.rootPc);
430
457
  if (tonic.pitchClass !== rootPc) {
431
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
458
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
432
459
  `tonic ${tonic.name} does not match the scale root pitch class ${rootPc}; pass a tonic that spells the scale root, or omit it to have one chosen`
433
460
  );
434
461
  }
435
462
  this.#scale = { rootPc, modeMask12: scale.modeMask12 };
436
463
  this.#tonic = tonic;
464
+ this.#variant = variant;
465
+ }
466
+ /** Preserve detection metadata while replacing its plain key with this API's Key. */
467
+ static #fromMatch(match) {
468
+ const key = new _Key(
469
+ match.key,
470
+ bestTonicForScale(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, match.key.rootPc), match.key),
471
+ match.variant
472
+ );
473
+ return { ...match, key };
437
474
  }
438
475
  /**
439
476
  * A major key.
@@ -446,9 +483,9 @@ var Key = class _Key {
446
483
  static major(root) {
447
484
  if (typeof root === "string") {
448
485
  const tonic = Note.of(root);
449
- return new _Key(_chunkFNDWXRRIcjs.majorKey.call(void 0, tonic.pitchClass), tonic);
486
+ return new _Key(_chunkBSLTLGM3cjs.majorKey.call(void 0, tonic.pitchClass), tonic);
450
487
  }
451
- const scale = _chunkFNDWXRRIcjs.majorKey.call(void 0, root);
488
+ const scale = _chunkBSLTLGM3cjs.majorKey.call(void 0, root);
452
489
  return new _Key(scale, bestTonicForScale(root, scale));
453
490
  }
454
491
  /**
@@ -462,9 +499,9 @@ var Key = class _Key {
462
499
  static minor(root) {
463
500
  if (typeof root === "string") {
464
501
  const tonic = Note.of(root);
465
- return new _Key(_chunkFNDWXRRIcjs.minorKey.call(void 0, tonic.pitchClass), tonic);
502
+ return new _Key(_chunkBSLTLGM3cjs.minorKey.call(void 0, tonic.pitchClass), tonic);
466
503
  }
467
- const scale = _chunkFNDWXRRIcjs.minorKey.call(void 0, root);
504
+ const scale = _chunkBSLTLGM3cjs.minorKey.call(void 0, root);
468
505
  return new _Key(scale, bestTonicForScale(root, scale));
469
506
  }
470
507
  /**
@@ -480,10 +517,10 @@ var Key = class _Key {
480
517
  static named(name, root) {
481
518
  if (typeof root === "string") {
482
519
  const tonic = Note.of(root);
483
- return new _Key(_chunkFNDWXRRIcjs.scaleByName.call(void 0, name, tonic.pitchClass), tonic);
520
+ return new _Key(_chunkBSLTLGM3cjs.scaleByName.call(void 0, name, tonic.pitchClass), tonic);
484
521
  }
485
- const scale = _chunkFNDWXRRIcjs.scaleByName.call(void 0, name, _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, root));
486
- return new _Key(scale, bestTonicForScale(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, root), scale));
522
+ const scale = _chunkBSLTLGM3cjs.scaleByName.call(void 0, name, _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, root));
523
+ return new _Key(scale, bestTonicForScale(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, root), scale));
487
524
  }
488
525
  /**
489
526
  * Wrap an existing `KeyScale`, synthesizing a spelled tonic when none is
@@ -500,7 +537,7 @@ var Key = class _Key {
500
537
  * @throws If the given tonic is not the scale's root pitch class.
501
538
  */
502
539
  static of(scale, tonic) {
503
- return new _Key(scale, _nullishCoalesce(tonic, () => ( bestTonicForScale(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, scale.rootPc), scale))));
540
+ return new _Key(scale, _nullishCoalesce(tonic, () => ( bestTonicForScale(_chunkPI3UXRWKcjs.pitchClassOf.call(void 0, scale.rootPc), scale))));
504
541
  }
505
542
  /**
506
543
  * Rebuild a key from its {@link Key.toJSON} output.
@@ -521,7 +558,17 @@ var Key = class _Key {
521
558
  * @returns Ranked keys (may be empty).
522
559
  */
523
560
  static detect(pitches, opts) {
524
- return _chunkCAN76NCUcjs.detectKey.call(void 0, pitches, opts).map((match) => _Key.of(match.key));
561
+ return _Key.detectMatches(pitches, opts).map((match) => match.key);
562
+ }
563
+ /**
564
+ * Identify keys while retaining each candidate's score, fit, and scale form.
565
+ *
566
+ * @param pitches MIDI pitches or bare pitch classes.
567
+ * @param opts How to weigh the input; see {@link DetectKeyOptions}.
568
+ * @returns Ranked key matches (may be empty).
569
+ */
570
+ static detectMatches(pitches, opts) {
571
+ return _chunkKQN2ZYNDcjs.detectKey.call(void 0, pitches, opts).map((match) => _Key.#fromMatch(match));
525
572
  }
526
573
  /**
527
574
  * The single best key interpretation of a pitch set.
@@ -536,8 +583,8 @@ var Key = class _Key {
536
583
  * ```
537
584
  */
538
585
  static detectBest(pitches, opts) {
539
- const best = _chunkCAN76NCUcjs.detectKeyBest.call(void 0, pitches, opts);
540
- return best === null ? null : _Key.of(best.key);
586
+ const best = _chunkKQN2ZYNDcjs.detectKeyBest.call(void 0, pitches, opts);
587
+ return best === null ? null : _Key.#fromMatch(best).key;
541
588
  }
542
589
  /**
543
590
  * Whether another key has the same tonic pitch class and mode.
@@ -566,7 +613,11 @@ var Key = class _Key {
566
613
  }
567
614
  /** Whether the scale has a minor third and no major third. */
568
615
  get isMinor() {
569
- return _chunkCAN76NCUcjs.isMinorKey.call(void 0, this.#scale);
616
+ return _chunkKQN2ZYNDcjs.isMinorKey.call(void 0, this.#scale);
617
+ }
618
+ /** The detected scale form, when this key came from key detection. */
619
+ get variant() {
620
+ return this.#variant;
570
621
  }
571
622
  /**
572
623
  * The scale's pitch classes in ascending scale-degree order (degree 0 first).
@@ -574,7 +625,7 @@ var Key = class _Key {
574
625
  * @returns One pitch class per scale degree.
575
626
  */
576
627
  pitchClasses() {
577
- return _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, this.#scale);
628
+ return _chunkBSLTLGM3cjs.scaleTonesInDegreeOrder.call(void 0, this.#scale);
578
629
  }
579
630
  /**
580
631
  * Transpose the key by a number of semitones.
@@ -592,9 +643,13 @@ var Key = class _Key {
592
643
  * ```
593
644
  */
594
645
  transpose(semitones) {
595
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, semitones, "semitones");
596
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, this.#scale.rootPc + Math.round(semitones));
597
- return _Key.of({ rootPc, modeMask12: this.#scale.modeMask12 });
646
+ _chunkPI3UXRWKcjs.assertFiniteNumber.call(void 0, semitones, "semitones");
647
+ const rootPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, this.#scale.rootPc + Math.round(semitones));
648
+ return new _Key(
649
+ { rootPc, modeMask12: this.#scale.modeMask12 },
650
+ this.#tonic.transpose(semitones),
651
+ this.#variant
652
+ );
598
653
  }
599
654
  /**
600
655
  * The spelled scale, one note per degree (e.g. C D E F G A B for C major).
@@ -602,7 +657,7 @@ var Key = class _Key {
602
657
  * @returns Spelled octave-less notes in scale-degree order.
603
658
  */
604
659
  notes() {
605
- return _chunkLW53AHQUcjs.spellScale.call(void 0, this.#tonic.data, this.#scale).map((note) => new Note(note));
660
+ return _chunkOL3IHHSJcjs.spellScale.call(void 0, this.#tonic.data, this.#scale).map((note) => new Note(note));
606
661
  }
607
662
  /**
608
663
  * Alias of {@link Key.notes}.
@@ -636,7 +691,7 @@ var Key = class _Key {
636
691
  * an explicit `quality` for those.
637
692
  */
638
693
  chord(degree, quality) {
639
- const data = quality === void 0 ? _chunkFNDWXRRIcjs.diatonicTriad.call(void 0, degree, this.#scale) : _chunkFNDWXRRIcjs.chordFromDegree.call(void 0, degree, quality, this.#scale);
694
+ const data = quality === void 0 ? _chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, this.#scale) : _chunkBSLTLGM3cjs.chordFromDegree.call(void 0, degree, quality, this.#scale);
640
695
  return new Chord(data, this);
641
696
  }
642
697
  /**
@@ -648,7 +703,7 @@ var Key = class _Key {
648
703
  * degrees. Use {@link Key.chord} with an explicit quality instead.
649
704
  */
650
705
  diatonicTriad(degree) {
651
- return new Chord(_chunkFNDWXRRIcjs.diatonicTriad.call(void 0, degree, this.#scale), this);
706
+ return new Chord(_chunkBSLTLGM3cjs.diatonicTriad.call(void 0, degree, this.#scale), this);
652
707
  }
653
708
  /**
654
709
  * The diatonic seventh chord on a scale degree, carrying this key as context.
@@ -659,7 +714,7 @@ var Key = class _Key {
659
714
  * degrees. Use {@link Key.chord} with an explicit quality instead.
660
715
  */
661
716
  diatonicSeventh(degree) {
662
- return new Chord(_chunkFNDWXRRIcjs.diatonicSeventh.call(void 0, degree, this.#scale), this);
717
+ return new Chord(_chunkBSLTLGM3cjs.diatonicSeventh.call(void 0, degree, this.#scale), this);
663
718
  }
664
719
  /**
665
720
  * Build the chord denoted by a Roman numeral in this key (including applied
@@ -670,7 +725,7 @@ var Key = class _Key {
670
725
  * @throws If the numeral is not valid.
671
726
  */
672
727
  roman(text) {
673
- return new Chord(_chunkCAN76NCUcjs.romanToChord.call(void 0, text, this.#scale), this);
728
+ return new Chord(_chunkKQN2ZYNDcjs.romanToChord.call(void 0, text, this.#scale), this);
674
729
  }
675
730
  /**
676
731
  * Whether a pitch belongs to the scale.
@@ -679,7 +734,7 @@ var Key = class _Key {
679
734
  * @returns True if the pitch class is a scale tone.
680
735
  */
681
736
  contains(x) {
682
- return _chunkFNDWXRRIcjs.isScaleTone.call(void 0, typeof x === "number" ? x : x.pitchClass, this.#scale);
737
+ return _chunkBSLTLGM3cjs.isScaleTone.call(void 0, typeof x === "number" ? x : x.pitchClass, this.#scale);
683
738
  }
684
739
  /**
685
740
  * The plain key data, for JSON serialization.
@@ -696,12 +751,14 @@ var Key = class _Key {
696
751
  }
697
752
  /**
698
753
  * The key's tonic and mode, so a template literal or a log line reads as the
699
- * key. Only the major/minor distinction is named, since a mask does not carry
700
- * a scale name.
754
+ * key. Detected harmonic and melodic minor keys retain their scale form.
701
755
  *
702
756
  * @returns The name, e.g. `'C major'` or `'A minor'`.
703
757
  */
704
758
  toString() {
759
+ if (this.#variant === "harmonic" || this.#variant === "melodic") {
760
+ return `${this.#tonic.name} ${this.#variant} minor`;
761
+ }
705
762
  return `${this.#tonic.name} ${this.isMinor ? "minor" : "major"}`;
706
763
  }
707
764
  };
@@ -717,9 +774,13 @@ var Progression = class _Progression {
717
774
  * @param key Optional key context for analysis methods.
718
775
  */
719
776
  constructor(chords, key) {
720
- this.#chords = Object.freeze([...chords]);
777
+ this.#chords = Object.freeze(_Progression.#attachKey(chords, key));
721
778
  this.#key = key;
722
779
  }
780
+ /** Attach a progression key to chord members that do not already carry one. */
781
+ static #attachKey(chords, key) {
782
+ return key === void 0 ? [...chords] : chords.map((chord) => chord.key === void 0 ? chord.withKey(key) : chord);
783
+ }
723
784
  /**
724
785
  * Build a progression from the {@link ChordSpan} records the generators
725
786
  * return, keeping their order.
@@ -740,11 +801,10 @@ var Progression = class _Progression {
740
801
  * ```
741
802
  */
742
803
  static fromSpans(spans, key) {
743
- const chords = spans.map((span) => {
744
- const chord = Chord.from(_chunkFNDWXRRIcjs.makeChord.call(void 0, span.rootPc, span.quality, span.bassPc));
745
- return key === void 0 ? chord : chord.withKey(key);
746
- });
747
- return key === void 0 ? new _Progression(chords) : new _Progression(chords, key);
804
+ const chords = spans.map(
805
+ (span) => Chord.from(_chunkBSLTLGM3cjs.makeChord.call(void 0, span.rootPc, span.quality, span.bassPc))
806
+ );
807
+ return new _Progression(chords, key);
748
808
  }
749
809
  /**
750
810
  * Rebuild a progression from its {@link Progression.toJSON} output.
@@ -831,21 +891,23 @@ var Progression = class _Progression {
831
891
  * @throws If any chord admits no voicing within the given ranges.
832
892
  */
833
893
  voice(opts) {
834
- return _chunkLW53AHQUcjs.voiceProgression.call(void 0,
894
+ const key = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _2 => _2.key]), () => ( _optionalChain([this, 'access', _3 => _3.#key, 'optionalAccess', _4 => _4.scale])));
895
+ return _chunkOL3IHHSJcjs.voiceProgression.call(void 0,
835
896
  this.#chords.map((chord) => chord.data),
836
- opts
897
+ key === void 0 ? opts : { ...opts, key }
837
898
  );
838
899
  }
839
900
  /**
840
901
  * The Roman numeral of each chord in a key.
841
902
  *
842
903
  * @param key Key to analyze in; falls back to the carried context.
904
+ * @param opts Applied-numeral rendering options.
843
905
  * @returns One numeral per chord.
844
906
  * @throws If no key is given and none is carried.
845
907
  */
846
- roman(key) {
908
+ roman(key, opts) {
847
909
  const resolved = this.#resolveKey(key);
848
- return this.#chords.map((chord) => chord.roman(resolved));
910
+ return this.#chords.map((chord) => chord.roman(resolved, opts));
849
911
  }
850
912
  /**
851
913
  * The harmonic function of each chord in a key.
@@ -865,17 +927,26 @@ var Progression = class _Progression {
865
927
  * progression has fewer than two chords.
866
928
  *
867
929
  * @param key Key to analyze in; falls back to the carried context.
930
+ * @param opts Applied-numeral rendering options.
868
931
  * @returns Per-chord analyses and the closing cadence.
869
932
  * @throws If no key is given and none is carried.
870
933
  */
871
- analyze(key) {
934
+ analyze(key, opts) {
872
935
  const resolved = this.#resolveKey(key);
873
- const chords = this.#chords.map((chord) => chord.analyze(resolved));
936
+ const chords = this.#chords.map((chord) => chord.analyze(resolved, opts));
874
937
  const from = this.#chords[this.#chords.length - 2];
875
938
  const to = this.#chords[this.#chords.length - 1];
876
- const cadence = from !== void 0 && to !== void 0 ? _chunkCAN76NCUcjs.detectCadence.call(void 0, from.data, to.data, resolved.scale) : null;
939
+ const cadence = from !== void 0 && to !== void 0 ? _chunkKQN2ZYNDcjs.detectCadence.call(void 0, from.data, to.data, resolved.scale) : null;
877
940
  return { chords, cadence };
878
941
  }
942
+ /**
943
+ * Choose one compatible scale for every chord, favoring smooth changes.
944
+ *
945
+ * @returns One scale choice per chord in this progression.
946
+ */
947
+ scales() {
948
+ return _chunkOL3IHHSJcjs.scalesForChanges.call(void 0, this.#chords.map((chord) => chord.data));
949
+ }
879
950
  /**
880
951
  * The plain progression data, for JSON serialization.
881
952
  *
@@ -900,12 +971,12 @@ var Progression = class _Progression {
900
971
  * ```
901
972
  */
902
973
  transpose(semitones) {
903
- const key = _optionalChain([this, 'access', _2 => _2.#key, 'optionalAccess', _3 => _3.transpose, 'call', _4 => _4(semitones)]);
974
+ const key = _optionalChain([this, 'access', _5 => _5.#key, 'optionalAccess', _6 => _6.transpose, 'call', _7 => _7(semitones)]);
904
975
  const chords = this.#chords.map((chord) => {
905
976
  const moved = chord.transpose(semitones);
906
977
  return key !== void 0 && moved.key === void 0 ? moved.withKey(key) : moved;
907
978
  });
908
- return key === void 0 ? new _Progression(chords) : new _Progression(chords, key);
979
+ return new _Progression(chords, key);
909
980
  }
910
981
  /**
911
982
  * The chord symbols separated by spaces, so a template literal or a log line
@@ -916,17 +987,25 @@ var Progression = class _Progression {
916
987
  toString() {
917
988
  return this.#chords.map((chord) => chord.symbol()).join(" ");
918
989
  }
990
+ /**
991
+ * The plain progression data, for JSON serialization.
992
+ *
993
+ * Private class fields do not serialize, so this preserves both the chord
994
+ * data and any carried key in `JSON.stringify(progression)`.
995
+ *
996
+ * @returns The chord data sequence and the carried key, if any.
997
+ */
919
998
  toJSON() {
920
999
  return {
921
- chords: this.#chords.map((chord) => chord.data),
922
- key: _optionalChain([this, 'access', _5 => _5.#key, 'optionalAccess', _6 => _6.toJSON, 'call', _7 => _7()])
1000
+ chords: this.#chords.map((chord) => chord.toJSON()),
1001
+ key: _optionalChain([this, 'access', _8 => _8.#key, 'optionalAccess', _9 => _9.toJSON, 'call', _10 => _10()])
923
1002
  };
924
1003
  }
925
1004
  /** Resolve the key for an analysis method: explicit first, then carried. */
926
1005
  #resolveKey(key) {
927
1006
  const resolved = _nullishCoalesce(key, () => ( this.#key));
928
1007
  if (resolved === void 0) {
929
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
1008
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
930
1009
  "progression has no key context; pass a Key or attach one with withKey()"
931
1010
  );
932
1011
  }
@@ -936,7 +1015,14 @@ var Progression = class _Progression {
936
1015
 
937
1016
  // src/model/chord.ts
938
1017
  function hintMatches(hint, pc) {
939
- return hint !== void 0 && pc !== void 0 && _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, hint)) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc);
1018
+ return hint !== void 0 && pc !== void 0 && _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, hint)) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, pc);
1019
+ }
1020
+ function transposeHint(hint, semitones) {
1021
+ if (hint === void 0) {
1022
+ return void 0;
1023
+ }
1024
+ const moved = _chunkPI3UXRWKcjs.transposeNote.call(void 0, hint, semitones);
1025
+ return { letter: moved.letter, alter: moved.alter };
940
1026
  }
941
1027
  function copyChord(data) {
942
1028
  const copy = {
@@ -962,8 +1048,8 @@ function deriveBassSpelling(chord, key) {
962
1048
  }
963
1049
  const root = chord.rootSpelling;
964
1050
  if (root !== void 0) {
965
- const index = chord.intervals.findIndex((i) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + i) === bassPc);
966
- const tone = index >= 0 ? _chunkLW53AHQUcjs.spellChordFromRoot.call(void 0, chord, root)[index] : void 0;
1051
+ const index = chord.intervals.findIndex((i) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, chord.rootPc + i) === bassPc);
1052
+ const tone = index >= 0 ? _chunkOL3IHHSJcjs.spellChordFromRoot.call(void 0, chord, root)[index] : void 0;
967
1053
  if (tone !== void 0) {
968
1054
  return { letter: tone.letter, alter: tone.alter };
969
1055
  }
@@ -971,7 +1057,7 @@ function deriveBassSpelling(chord, key) {
971
1057
  if (key === void 0) {
972
1058
  return void 0;
973
1059
  }
974
- const spelled = _chunkLW53AHQUcjs.spellPitchClass.call(void 0, bassPc, key.tonic.data, key.scale);
1060
+ const spelled = _chunkOL3IHHSJcjs.spellPitchClass.call(void 0, bassPc, key.tonic.data, key.scale);
975
1061
  return { letter: spelled.letter, alter: spelled.alter };
976
1062
  }
977
1063
  var Chord = class _Chord {
@@ -996,10 +1082,13 @@ var Chord = class _Chord {
996
1082
  * first key's letters forever. A spelling the caller supplied always wins.
997
1083
  */
998
1084
  get #data() {
1085
+ return this.#dataWithKey(this.#key);
1086
+ }
1087
+ /** Build a read view whose missing spellings are derived from `key`. */
1088
+ #dataWithKey(key) {
999
1089
  const out = copyChord(this.#given);
1000
- const key = this.#key;
1001
1090
  if (out.rootSpelling === void 0 && key !== void 0) {
1002
- out.rootSpelling = _chunkLW53AHQUcjs.spellPitchClass.call(void 0, out.rootPc, key.tonic.data, key.scale);
1091
+ out.rootSpelling = _chunkOL3IHHSJcjs.spellPitchClass.call(void 0, out.rootPc, key.tonic.data, key.scale);
1003
1092
  }
1004
1093
  if (out.bassSpelling === void 0) {
1005
1094
  const bass = deriveBassSpelling(out, key);
@@ -1022,7 +1111,7 @@ var Chord = class _Chord {
1022
1111
  const bassNote = typeof bass === "string" ? Note.of(bass) : void 0;
1023
1112
  const bassPc = bassNote !== void 0 ? bassNote.pitchClass : bass;
1024
1113
  const rootNote = typeof root === "string" ? Note.of(root) : void 0;
1025
- const data = _chunkFNDWXRRIcjs.makeChord.call(void 0,
1114
+ const data = _chunkBSLTLGM3cjs.makeChord.call(void 0,
1026
1115
  rootNote !== void 0 ? rootNote.pitchClass : root,
1027
1116
  quality,
1028
1117
  bassPc
@@ -1074,7 +1163,7 @@ var Chord = class _Chord {
1074
1163
  * @throws If the root or quality is not recognized.
1075
1164
  */
1076
1165
  static parse(symbol) {
1077
- return new _Chord(_chunkLW53AHQUcjs.parseChordSymbol.call(void 0, symbol));
1166
+ return new _Chord(_chunkOL3IHHSJcjs.parseChordSymbol.call(void 0, symbol));
1078
1167
  }
1079
1168
  /**
1080
1169
  * Identify the chords matching a set of pitches, best interpretation first.
@@ -1084,8 +1173,8 @@ var Chord = class _Chord {
1084
1173
  * @returns Ranked chord interpretations (may be empty).
1085
1174
  */
1086
1175
  static detect(pitches, opts) {
1087
- return _chunkCAN76NCUcjs.detectChord.call(void 0, pitches, opts).map(
1088
- (match) => new _Chord(_chunkFNDWXRRIcjs.makeChord.call(void 0, match.rootPc, match.quality, match.bassPc))
1176
+ return _chunkKQN2ZYNDcjs.detectChord.call(void 0, pitches, opts).map(
1177
+ (match) => new _Chord(_chunkBSLTLGM3cjs.makeChord.call(void 0, match.rootPc, match.quality, match.bassPc))
1089
1178
  );
1090
1179
  }
1091
1180
  /**
@@ -1107,8 +1196,8 @@ var Chord = class _Chord {
1107
1196
  * ```
1108
1197
  */
1109
1198
  static detectMatches(pitches, opts) {
1110
- return _chunkCAN76NCUcjs.detectChord.call(void 0, pitches, opts).map((match) => ({
1111
- chord: new _Chord(_chunkFNDWXRRIcjs.makeChord.call(void 0, match.rootPc, match.quality, match.bassPc)),
1199
+ return _chunkKQN2ZYNDcjs.detectChord.call(void 0, pitches, opts).map((match) => ({
1200
+ chord: new _Chord(_chunkBSLTLGM3cjs.makeChord.call(void 0, match.rootPc, match.quality, match.bassPc)),
1112
1201
  match
1113
1202
  }));
1114
1203
  }
@@ -1120,7 +1209,7 @@ var Chord = class _Chord {
1120
1209
  * @returns The top-ranked chord, or null when nothing matches.
1121
1210
  */
1122
1211
  static detectBest(pitches, opts) {
1123
- const best = _chunkCAN76NCUcjs.detectChordBest.call(void 0, pitches, opts);
1212
+ const best = _chunkKQN2ZYNDcjs.detectChordBest.call(void 0, pitches, opts);
1124
1213
  return best === null ? null : new _Chord(best);
1125
1214
  }
1126
1215
  /** The root pitch class (0..11). */
@@ -1171,17 +1260,18 @@ var Chord = class _Chord {
1171
1260
  * @returns Pitch classes ascending in [0, 11].
1172
1261
  */
1173
1262
  pitchClasses() {
1174
- return _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, this.#data);
1263
+ return _chunkBSLTLGM3cjs.chordPitchClasses.call(void 0, this.#data);
1175
1264
  }
1176
1265
  /**
1177
1266
  * The chord's Roman numeral in a key.
1178
1267
  *
1179
1268
  * @param key Key to analyze in; falls back to the carried context.
1269
+ * @param opts Applied-numeral rendering options.
1180
1270
  * @returns The Roman numeral string.
1181
1271
  * @throws If no key is given and none is carried.
1182
1272
  */
1183
- roman(key) {
1184
- return _chunkCAN76NCUcjs.chordToRoman.call(void 0, this.#data, this.#resolveKey(key).scale);
1273
+ roman(key, opts) {
1274
+ return _chunkKQN2ZYNDcjs.chordToRoman.call(void 0, this.#data, this.#resolveKey(key).scale, opts);
1185
1275
  }
1186
1276
  /**
1187
1277
  * The chord's harmonic function (tonic / subdominant / dominant) in a key.
@@ -1191,17 +1281,18 @@ var Chord = class _Chord {
1191
1281
  * @throws If no key is given and none is carried.
1192
1282
  */
1193
1283
  function(key) {
1194
- return _chunkCAN76NCUcjs.functionOf.call(void 0, this.#data, this.#resolveKey(key).scale);
1284
+ return _chunkKQN2ZYNDcjs.functionOf.call(void 0, this.#data, this.#resolveKey(key).scale);
1195
1285
  }
1196
1286
  /**
1197
1287
  * Full functional analysis: function, borrowing, and Roman numeral.
1198
1288
  *
1199
1289
  * @param key Key to analyze in; falls back to the carried context.
1290
+ * @param opts Applied-numeral rendering options.
1200
1291
  * @returns The chord analysis.
1201
1292
  * @throws If no key is given and none is carried.
1202
1293
  */
1203
- analyze(key) {
1204
- return _chunkCAN76NCUcjs.analyzeChord.call(void 0, this.#data, this.#resolveKey(key).scale);
1294
+ analyze(key, opts) {
1295
+ return _chunkKQN2ZYNDcjs.analyzeChord.call(void 0, this.#data, this.#resolveKey(key).scale, opts);
1205
1296
  }
1206
1297
  /**
1207
1298
  * Whether the chord is borrowed from the parallel mode (modal interchange).
@@ -1211,7 +1302,7 @@ var Chord = class _Chord {
1211
1302
  * @throws If no key is given and none is carried.
1212
1303
  */
1213
1304
  isBorrowed(key) {
1214
- return _chunkCAN76NCUcjs.isBorrowedChord.call(void 0, this.#data, this.#resolveKey(key).scale);
1305
+ return _chunkKQN2ZYNDcjs.isBorrowedChord.call(void 0, this.#data, this.#resolveKey(key).scale);
1215
1306
  }
1216
1307
  /**
1217
1308
  * The origin of a non-diatonic chord (parallel mode or Neapolitan), or null.
@@ -1221,7 +1312,7 @@ var Chord = class _Chord {
1221
1312
  * @throws If no key is given and none is carried.
1222
1313
  */
1223
1314
  borrowedSource(key) {
1224
- return _chunkCAN76NCUcjs.borrowedSource.call(void 0, this.#data, this.#resolveKey(key).scale);
1315
+ return _chunkKQN2ZYNDcjs.borrowedSource.call(void 0, this.#data, this.#resolveKey(key).scale);
1225
1316
  }
1226
1317
  /**
1227
1318
  * The chord rendered as a lead-sheet symbol (e.g. `'Cmaj7'`, `'F#m7'`, `'C/G'`).
@@ -1230,7 +1321,7 @@ var Chord = class _Chord {
1230
1321
  * @returns The chord symbol.
1231
1322
  */
1232
1323
  symbol(opts) {
1233
- return _chunkLW53AHQUcjs.formatChordSymbol.call(void 0, this.#data, opts);
1324
+ return _chunkOL3IHHSJcjs.formatChordSymbol.call(void 0, this.#data, opts);
1234
1325
  }
1235
1326
  /**
1236
1327
  * Realize the chord as one MIDI pitch per voice, ascending.
@@ -1243,8 +1334,8 @@ var Chord = class _Chord {
1243
1334
  * @throws If no voicing fits the given ranges.
1244
1335
  */
1245
1336
  voice(opts) {
1246
- const key = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _8 => _8.key]), () => ( _optionalChain([this, 'access', _9 => _9.#key, 'optionalAccess', _10 => _10.scale])));
1247
- return _chunkLW53AHQUcjs.voiceChord.call(void 0, this.#data, key === void 0 ? opts : { ...opts, key });
1337
+ const key = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _11 => _11.key]), () => ( _optionalChain([this, 'access', _12 => _12.#key, 'optionalAccess', _13 => _13.scale])));
1338
+ return _chunkOL3IHHSJcjs.voiceChord.call(void 0, this.#data, key === void 0 ? opts : { ...opts, key });
1248
1339
  }
1249
1340
  /**
1250
1341
  * Realize the chord as a single styled voicing (`close`, `drop2`, `drop3`,
@@ -1254,7 +1345,7 @@ var Chord = class _Chord {
1254
1345
  * @returns MIDI pitches, ascending.
1255
1346
  */
1256
1347
  styledVoicing(opts) {
1257
- return _chunkLW53AHQUcjs.voiceChordStyled.call(void 0, this.#data, opts);
1348
+ return _chunkOL3IHHSJcjs.voiceChordStyled.call(void 0, this.#data, opts);
1258
1349
  }
1259
1350
  /**
1260
1351
  * The negative-harmony mirror of the chord about the key's tonic–dominant
@@ -1267,7 +1358,7 @@ var Chord = class _Chord {
1267
1358
  */
1268
1359
  negativeHarmony(key) {
1269
1360
  const resolved = this.#resolveKey(key);
1270
- return new _Chord(_chunkZEJR4TKLcjs.negativeHarmonyMirror.call(void 0, this.#data, resolved.scale), _nullishCoalesce(key, () => ( this.#key)));
1361
+ return new _Chord(_chunk357ZDPVUcjs.negativeHarmonyMirror.call(void 0, this.#data, resolved.scale), _nullishCoalesce(key, () => ( this.#key)));
1271
1362
  }
1272
1363
  /**
1273
1364
  * The n-th inversion: a copy whose bass is the chord tone `n` steps above the
@@ -1286,14 +1377,14 @@ var Chord = class _Chord {
1286
1377
  const intervals = data.intervals;
1287
1378
  const length = intervals.length;
1288
1379
  if (length === 0) {
1289
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("cannot invert a chord with no intervals");
1380
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)("cannot invert a chord with no intervals");
1290
1381
  }
1291
1382
  const index = (n % length + length) % length;
1292
1383
  delete data.bassSpelling;
1293
1384
  if (index === 0) {
1294
1385
  delete data.bassPc;
1295
1386
  } else {
1296
- data.bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, data.rootPc + (_nullishCoalesce(intervals[index], () => ( 0))));
1387
+ data.bassPc = _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, data.rootPc + (_nullishCoalesce(intervals[index], () => ( 0))));
1297
1388
  }
1298
1389
  return new _Chord(data, this.#key);
1299
1390
  }
@@ -1314,8 +1405,16 @@ var Chord = class _Chord {
1314
1405
  * ```
1315
1406
  */
1316
1407
  transpose(semitones) {
1317
- const moved = _chunkFNDWXRRIcjs.transposeChord.call(void 0, this.#given, semitones);
1318
- return new _Chord(moved, _optionalChain([this, 'access', _11 => _11.#key, 'optionalAccess', _12 => _12.transpose, 'call', _13 => _13(semitones)]));
1408
+ const moved = _chunkBSLTLGM3cjs.transposeChord.call(void 0, this.#given, semitones);
1409
+ const rootSpelling = transposeHint(this.#given.rootSpelling, semitones);
1410
+ const bassSpelling = transposeHint(this.#given.bassSpelling, semitones);
1411
+ if (rootSpelling !== void 0) {
1412
+ moved.rootSpelling = rootSpelling;
1413
+ }
1414
+ if (bassSpelling !== void 0) {
1415
+ moved.bassSpelling = bassSpelling;
1416
+ }
1417
+ return new _Chord(moved, _optionalChain([this, 'access', _14 => _14.#key, 'optionalAccess', _15 => _15.transpose, 'call', _16 => _16(semitones)]));
1319
1418
  }
1320
1419
  /**
1321
1420
  * The named scales that fit over this chord, best fit first, rooted on the
@@ -1324,7 +1423,7 @@ var Chord = class _Chord {
1324
1423
  * @returns The matching scales.
1325
1424
  */
1326
1425
  scales() {
1327
- return _chunkLW53AHQUcjs.chordScales.call(void 0, this.#data);
1426
+ return _chunkOL3IHHSJcjs.chordScales.call(void 0, this.#data);
1328
1427
  }
1329
1428
  /**
1330
1429
  * The available tensions (usable non-chord, non-avoid scale tones) of a
@@ -1335,7 +1434,7 @@ var Chord = class _Chord {
1335
1434
  * @throws If `scaleName` is not a built-in scale.
1336
1435
  */
1337
1436
  tensions(scaleName) {
1338
- return _chunkLW53AHQUcjs.availableTensions.call(void 0, this.#data, scaleName);
1437
+ return _chunkOL3IHHSJcjs.availableTensions.call(void 0, this.#data, scaleName);
1339
1438
  }
1340
1439
  /**
1341
1440
  * The avoid notes (scale tones a semitone above a chord tone) of a scale
@@ -1346,7 +1445,7 @@ var Chord = class _Chord {
1346
1445
  * @throws If `scaleName` is not a built-in scale.
1347
1446
  */
1348
1447
  avoidNotes(scaleName) {
1349
- return _chunkLW53AHQUcjs.avoidNotes.call(void 0, this.#data, scaleName);
1448
+ return _chunkOL3IHHSJcjs.avoidNotes.call(void 0, this.#data, scaleName);
1350
1449
  }
1351
1450
  /**
1352
1451
  * Spell the chord tones with letter names, root first, in the key's spelling.
@@ -1368,23 +1467,23 @@ var Chord = class _Chord {
1368
1467
  * spelling of its own.
1369
1468
  */
1370
1469
  spell(key) {
1371
- const data = this.#data;
1372
1470
  const resolved = _nullishCoalesce(key, () => ( this.#key));
1471
+ const data = this.#dataWithKey(resolved);
1373
1472
  let tones;
1374
1473
  if (resolved === void 0) {
1375
1474
  const root = data.rootSpelling;
1376
1475
  if (root === void 0) {
1377
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
1476
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
1378
1477
  "chord has no key context and no root spelling; pass a Key, attach one with withKey(), or build the chord from a symbol"
1379
1478
  );
1380
1479
  }
1381
- tones = _chunkLW53AHQUcjs.spellChordFromRoot.call(void 0, data, root);
1480
+ tones = _chunkOL3IHHSJcjs.spellChordFromRoot.call(void 0, data, root);
1382
1481
  } else {
1383
- tones = _chunkLW53AHQUcjs.spellChord.call(void 0, data, resolved.tonic.data, resolved.scale);
1482
+ tones = _chunkOL3IHHSJcjs.spellChord.call(void 0, data, resolved.tonic.data, resolved.scale);
1384
1483
  }
1385
1484
  const bassPc = data.bassPc;
1386
1485
  const bass = data.bassSpelling;
1387
- if (bassPc !== void 0 && bass !== void 0 && !tones.some((tone) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, tone)) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, bassPc))) {
1486
+ if (bassPc !== void 0 && bass !== void 0 && !tones.some((tone) => _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, _chunkPI3UXRWKcjs.noteToPitchClass.call(void 0, tone)) === _chunkPI3UXRWKcjs.pitchClassOf.call(void 0, bassPc))) {
1388
1487
  tones = [...tones, { letter: bass.letter, alter: bass.alter }];
1389
1488
  }
1390
1489
  return tones.map((note) => new Note(note));
@@ -1417,7 +1516,7 @@ var Chord = class _Chord {
1417
1516
  * @returns A copy of the underlying plain chord object.
1418
1517
  */
1419
1518
  toJSON() {
1420
- return this.data;
1519
+ return copyChord(this.#given);
1421
1520
  }
1422
1521
  /**
1423
1522
  * The chord symbol, so a template literal or a log line reads as the chord.
@@ -1431,7 +1530,7 @@ var Chord = class _Chord {
1431
1530
  #resolveKey(key) {
1432
1531
  const resolved = _nullishCoalesce(key, () => ( this.#key));
1433
1532
  if (resolved === void 0) {
1434
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
1533
+ throw new (0, _chunkPI3UXRWKcjs.InvalidInputError)(
1435
1534
  "chord has no key context; pass a Key or attach one with withKey()"
1436
1535
  );
1437
1536
  }
@@ -1446,4 +1545,4 @@ var Chord = class _Chord {
1446
1545
 
1447
1546
 
1448
1547
  exports.Interval = Interval; exports.Note = Note; exports.Key = Key; exports.Progression = Progression; exports.Chord = Chord;
1449
- //# sourceMappingURL=chunk-NVE34YGZ.cjs.map
1548
+ //# sourceMappingURL=chunk-WTAN4ACG.cjs.map