@libraz/libcantus 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +4 -0
  2. package/README_ja.md +4 -0
  3. package/dist/analyze/index.cjs +31 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +155 -9
  6. package/dist/analyze/index.d.ts +155 -9
  7. package/dist/analyze/index.js +37 -13
  8. package/dist/{chunk-TT6PKMPR.cjs → chunk-35NBV2FT.cjs} +16 -16
  9. package/dist/{chunk-TT6PKMPR.cjs.map → chunk-35NBV2FT.cjs.map} +1 -1
  10. package/dist/{chunk-YPB3A3EY.cjs → chunk-3R3QVKY5.cjs} +314 -285
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-GDKE6RQW.cjs → chunk-4R2LWV7E.cjs} +691 -413
  13. package/dist/chunk-4R2LWV7E.cjs.map +1 -0
  14. package/dist/{chunk-RVS4G4MR.cjs → chunk-76ZBTRG2.cjs} +198 -198
  15. package/dist/{chunk-RVS4G4MR.cjs.map → chunk-76ZBTRG2.cjs.map} +1 -1
  16. package/dist/{chunk-X6RJAHII.js → chunk-7KG3RTYS.js} +2 -2
  17. package/dist/{chunk-PQL7E5F6.js → chunk-AGOVW7U6.js} +1 -1
  18. package/dist/{chunk-PQL7E5F6.js.map → chunk-AGOVW7U6.js.map} +1 -1
  19. package/dist/{chunk-5KW7DNCJ.cjs → chunk-B2PW6EHN.cjs} +2423 -453
  20. package/dist/chunk-B2PW6EHN.cjs.map +1 -0
  21. package/dist/{chunk-J772DL3B.cjs → chunk-CGBADIO5.cjs} +53 -53
  22. package/dist/{chunk-J772DL3B.cjs.map → chunk-CGBADIO5.cjs.map} +1 -1
  23. package/dist/{chunk-R5VUXQMP.js → chunk-CPMB7P75.js} +1978 -8
  24. package/dist/chunk-CPMB7P75.js.map +1 -0
  25. package/dist/{chunk-VLE5R7PF.js → chunk-GNVN4PPJ.js} +1874 -8
  26. package/dist/chunk-GNVN4PPJ.js.map +1 -0
  27. package/dist/{chunk-Q6R3QAZD.cjs → chunk-IFZDWIKM.cjs} +2199 -333
  28. package/dist/chunk-IFZDWIKM.cjs.map +1 -0
  29. package/dist/{chunk-DKICR7AN.js → chunk-IPEF6NWB.js} +296 -18
  30. package/dist/chunk-IPEF6NWB.js.map +1 -0
  31. package/dist/{chunk-AWEMD747.js → chunk-M5RYMAFI.js} +3 -3
  32. package/dist/{chunk-HSY2VQXZ.js → chunk-MLBPXA4Z.js} +330 -728
  33. package/dist/chunk-MLBPXA4Z.js.map +1 -0
  34. package/dist/{chunk-H66ARFU5.js → chunk-NB25P3EH.js} +3 -3
  35. package/dist/{chunk-5JJ6EHEZ.cjs → chunk-NM7UAMC3.cjs} +62 -62
  36. package/dist/{chunk-5JJ6EHEZ.cjs.map → chunk-NM7UAMC3.cjs.map} +1 -1
  37. package/dist/{chunk-R7B5N2JK.cjs → chunk-PM4XJE2Y.cjs} +47 -47
  38. package/dist/{chunk-R7B5N2JK.cjs.map → chunk-PM4XJE2Y.cjs.map} +1 -1
  39. package/dist/{chunk-7GOKQJTQ.js → chunk-PUKJP2Z5.js} +2 -2
  40. package/dist/{chunk-QCXYKSZS.cjs → chunk-PWC4CRWT.cjs} +19 -19
  41. package/dist/{chunk-QCXYKSZS.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
  42. package/dist/{chunk-FPBZFNCD.js → chunk-RFIBF2RR.js} +47 -18
  43. package/dist/{chunk-FPBZFNCD.js.map → chunk-RFIBF2RR.js.map} +1 -1
  44. package/dist/{chunk-XKSO2V5I.cjs → chunk-S5BKZZGM.cjs} +1 -1
  45. package/dist/{chunk-XKSO2V5I.cjs.map → chunk-S5BKZZGM.cjs.map} +1 -1
  46. package/dist/{chunk-HOHFYGKW.js → chunk-UMWZ4ZET.js} +2 -2
  47. package/dist/{chunk-XVMJO3QF.cjs → chunk-YFA7X2F6.cjs} +489 -887
  48. package/dist/chunk-YFA7X2F6.cjs.map +1 -0
  49. package/dist/{chunk-57WB2EDF.js → chunk-ZBUROTJQ.js} +3 -3
  50. package/dist/{coerce-wwdELNYS.d.ts → coerce-DP0vYu-x.d.ts} +77 -1
  51. package/dist/{coerce-CJlgULrQ.d.cts → coerce-DRERzdve.d.cts} +77 -1
  52. package/dist/core/index.cjs +5 -5
  53. package/dist/core/index.d.cts +2 -2
  54. package/dist/core/index.d.ts +2 -2
  55. package/dist/core/index.js +4 -4
  56. package/dist/generate/index.cjs +20 -8
  57. package/dist/generate/index.cjs.map +1 -1
  58. package/dist/generate/index.d.cts +87 -9
  59. package/dist/generate/index.d.ts +87 -9
  60. package/dist/generate/index.js +19 -7
  61. package/dist/{index-BaoKVVhE.d.cts → index-B9QLziDe.d.cts} +2 -2
  62. package/dist/{index-CnHQgqNh.d.ts → index-BHIUyjhZ.d.ts} +2 -2
  63. package/dist/{index-CBCP84rv.d.cts → index-BL5N2Oyz.d.cts} +1 -1
  64. package/dist/{index-D_hzxD8w.d.cts → index-BMweUlm5.d.cts} +3 -0
  65. package/dist/{index-Ci3xyows.d.ts → index-CL4b4gUQ.d.ts} +3 -0
  66. package/dist/{index-BzLNkmA7.d.cts → index-CY-3NVfy.d.cts} +486 -5
  67. package/dist/{index-CnIdP4EZ.d.ts → index-Dw2DP7vm.d.ts} +1 -1
  68. package/dist/{index-DpC-mDlV.d.ts → index-zaZdmnU4.d.ts} +486 -5
  69. package/dist/index.cjs +49 -13
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +10 -10
  72. package/dist/index.d.ts +10 -10
  73. package/dist/index.js +55 -19
  74. package/dist/{masks-O73Rhj5d.d.ts → masks-Cc7-MzzY.d.ts} +1 -1
  75. package/dist/{masks-C8TWLFIi.d.cts → masks-pADfTOiD.d.cts} +1 -1
  76. package/dist/model/index.cjs +12 -12
  77. package/dist/model/index.d.cts +213 -16
  78. package/dist/model/index.d.ts +213 -16
  79. package/dist/model/index.js +11 -11
  80. package/dist/{index-DTsKaPaC.d.cts → profile-CQMEqdH9.d.cts} +88 -601
  81. package/dist/{index-BUtIf2go.d.ts → profile-DLdlIpS9.d.ts} +88 -601
  82. package/dist/theory/index.cjs +5 -5
  83. package/dist/theory/index.d.cts +7 -7
  84. package/dist/theory/index.d.ts +7 -7
  85. package/dist/theory/index.js +4 -4
  86. package/dist/{index-BpWOhkkE.d.ts → types-D-XgxGvp.d.cts} +1170 -14
  87. package/dist/{index-B3WCj_Hm.d.cts → types-Dh7C064C.d.ts} +1170 -14
  88. package/package.json +6 -5
  89. package/dist/chunk-5KW7DNCJ.cjs.map +0 -1
  90. package/dist/chunk-DKICR7AN.js.map +0 -1
  91. package/dist/chunk-GDKE6RQW.cjs.map +0 -1
  92. package/dist/chunk-HSY2VQXZ.js.map +0 -1
  93. package/dist/chunk-Q6R3QAZD.cjs.map +0 -1
  94. package/dist/chunk-R5VUXQMP.js.map +0 -1
  95. package/dist/chunk-VLE5R7PF.js.map +0 -1
  96. package/dist/chunk-XVMJO3QF.cjs.map +0 -1
  97. package/dist/chunk-YPB3A3EY.cjs.map +0 -1
  98. /package/dist/{chunk-X6RJAHII.js.map → chunk-7KG3RTYS.js.map} +0 -0
  99. /package/dist/{chunk-AWEMD747.js.map → chunk-M5RYMAFI.js.map} +0 -0
  100. /package/dist/{chunk-H66ARFU5.js.map → chunk-NB25P3EH.js.map} +0 -0
  101. /package/dist/{chunk-7GOKQJTQ.js.map → chunk-PUKJP2Z5.js.map} +0 -0
  102. /package/dist/{chunk-HOHFYGKW.js.map → chunk-UMWZ4ZET.js.map} +0 -0
  103. /package/dist/{chunk-57WB2EDF.js.map → chunk-ZBUROTJQ.js.map} +0 -0
@@ -1,20 +1,16 @@
1
1
  import {
2
2
  analyzeArrangement,
3
3
  analyzePolyphony,
4
+ analyzeReference,
4
5
  createArrangementSession,
5
- extractMotifs,
6
6
  hypermeter,
7
- melodicContour,
8
- melodicSimilarity,
9
- motifFromNotes,
10
7
  phrasesFromTimeline,
11
- reduceProgression,
12
- relateMotifs,
8
+ referenceFromReadings,
13
9
  sectionsFromNotes,
14
10
  structuralCadences,
15
11
  tensionCurveFrom,
16
12
  trackRoleOf
17
- } from "./chunk-HSY2VQXZ.js";
13
+ } from "./chunk-MLBPXA4Z.js";
18
14
  import {
19
15
  TWELVE_TET,
20
16
  beatsToDuration,
@@ -38,18 +34,21 @@ import {
38
34
  tempoAt,
39
35
  tempoReader,
40
36
  ticksToBeats
41
- } from "./chunk-HOHFYGKW.js";
37
+ } from "./chunk-UMWZ4ZET.js";
42
38
  import {
43
39
  STEP_BEATS,
44
40
  applyGrooveTemplate,
45
41
  cellSpan,
46
42
  deform,
43
+ deriveCompositionPlan,
47
44
  developMotif,
48
45
  doubleTime,
46
+ evaluateComposition,
49
47
  extractGrooveTemplate,
50
48
  generateBassLine,
51
49
  generateCounterMelody,
52
50
  generateDrums,
51
+ generateMelody,
53
52
  generateMotif,
54
53
  generateProgression,
55
54
  generateRhythm,
@@ -69,9 +68,10 @@ import {
69
68
  thin,
70
69
  transformMotif,
71
70
  withinCeiling
72
- } from "./chunk-R5VUXQMP.js";
71
+ } from "./chunk-CPMB7P75.js";
73
72
  import {
74
73
  analyzeChord,
74
+ analyzeRhythm,
75
75
  augmentedSixthChord,
76
76
  borrowedSource,
77
77
  chordAtBeat,
@@ -87,17 +87,24 @@ import {
87
87
  detectKeyFromNotes,
88
88
  detectModulations,
89
89
  explainRoman,
90
+ extractMotifs,
90
91
  functionOf,
91
92
  isBorrowedChord,
92
93
  isMinorKey,
93
94
  keyLookup,
94
95
  keyTimelineFromNotes,
96
+ melodicContour,
97
+ melodicSimilarity,
98
+ motifFromNotes,
99
+ motifGraph,
95
100
  pivotChords,
96
101
  prevailingKeyOf,
102
+ reduceProgression,
103
+ relateMotifs,
97
104
  romanToChord,
98
105
  secondaryDominantOf,
99
106
  toVoiceNotes
100
- } from "./chunk-VLE5R7PF.js";
107
+ } from "./chunk-GNVN4PPJ.js";
101
108
  import {
102
109
  BASS_4_STRING,
103
110
  BASS_5_STRING,
@@ -110,10 +117,10 @@ import {
110
117
  soundsIn,
111
118
  toInstrumentProfile,
112
119
  toStringedProfile
113
- } from "./chunk-X6RJAHII.js";
120
+ } from "./chunk-7KG3RTYS.js";
114
121
  import {
115
122
  createNoteEventIndex
116
- } from "./chunk-7GOKQJTQ.js";
123
+ } from "./chunk-PUKJP2Z5.js";
117
124
  import {
118
125
  SATB_RANGES,
119
126
  checkPartWriting,
@@ -128,14 +135,14 @@ import {
128
135
  voiceChordStyled,
129
136
  voiceLeadingCost,
130
137
  voiceProgression
131
- } from "./chunk-AWEMD747.js";
138
+ } from "./chunk-M5RYMAFI.js";
132
139
  import {
133
140
  availableTensions,
134
141
  avoidNotes,
135
142
  chordScaleReport,
136
143
  chordScales,
137
144
  scalesForChanges
138
- } from "./chunk-H66ARFU5.js";
145
+ } from "./chunk-NB25P3EH.js";
139
146
  import {
140
147
  NAMED_SCALES,
141
148
  assertChordQuality,
@@ -191,7 +198,7 @@ import {
191
198
  tryResolveKeyName,
192
199
  variantOfMask,
193
200
  voiceIndependence
194
- } from "./chunk-FPBZFNCD.js";
201
+ } from "./chunk-RFIBF2RR.js";
195
202
  import {
196
203
  InvalidInputError,
197
204
  MAX_NAME_ACCIDENTALS,
@@ -235,7 +242,7 @@ import {
235
242
  tryParseTimeSignature,
236
243
  tuplet,
237
244
  unwrapParse
238
- } from "./chunk-PQL7E5F6.js";
245
+ } from "./chunk-AGOVW7U6.js";
239
246
 
240
247
  // src/model/interval.ts
241
248
  var INVERSION_SUM = 9;
@@ -4411,6 +4418,8 @@ var Score = class _Score {
4411
4418
  #chords;
4412
4419
  /** The key regions, made on the first member that needs them. */
4413
4420
  #regions;
4421
+ /** The reference profile asked for with no options, made on first read. */
4422
+ #profile;
4414
4423
  /**
4415
4424
  * Wrap plain score data.
4416
4425
  *
@@ -4898,6 +4907,143 @@ var Score = class _Score {
4898
4907
  contour() {
4899
4908
  return melodicContour(this.#data.notes);
4900
4909
  }
4910
+ /**
4911
+ * The score's structural fingerprint: form, phrase shape, harmonic function
4912
+ * and rhythm, and how its motifs derive from one another.
4913
+ *
4914
+ * The harmony is the score's own kept chord analysis, read once whether this
4915
+ * is asked before or after {@link Score.timeline} — unless `opts.timeline`
4916
+ * gives the harmony directly, in which case the reading runs fresh through
4917
+ * {@link analyzeReference} rather than the harmony this score already holds.
4918
+ * The meter, the key and the span are the score's own and are not on offer
4919
+ * here: a caller wanting another meter reads through {@link
4920
+ * Score.withMeters}, and one wanting another key or span calls {@link
4921
+ * analyzeReference} directly.
4922
+ *
4923
+ * Asked with no options, the answer is kept and a copy of it handed back, as
4924
+ * {@link Score.timeline} keeps its own reading; asked with options, the
4925
+ * answer is read afresh and not kept.
4926
+ *
4927
+ * @param opts The harmony, the melody, and the budget; see {@link
4928
+ * ReferenceProfileOptions}. Meter, key and span are the score's own.
4929
+ * @returns The profile.
4930
+ * @example
4931
+ * ```ts
4932
+ * import { Score } from '@libraz/libcantus';
4933
+ * const triad = (pitches: number[], startBeat: number) =>
4934
+ * pitches.map((pitch) => ({ pitch, startBeat, durationBeat: 4 }));
4935
+ * const score = Score.of(
4936
+ * [
4937
+ * ...triad([60, 64, 67], 0),
4938
+ * ...triad([65, 69, 72], 4),
4939
+ * ...triad([67, 71, 74], 8),
4940
+ * ...triad([60, 64, 67], 12),
4941
+ * ],
4942
+ * { key: 'C major' },
4943
+ * );
4944
+ * score.reference().harmony.chords.map((chord) => chord.roman); // ['I', 'IV', 'V', 'I']
4945
+ * ```
4946
+ */
4947
+ reference(opts) {
4948
+ if (opts === void 0 && this.#profile !== void 0) {
4949
+ return copyPlain(this.#profile, "reference profile");
4950
+ }
4951
+ const keyed = this.#key === void 0 ? {} : { key: keyIdentity(this.#key) };
4952
+ const asked = {
4953
+ meters: this.#data.meters,
4954
+ totalBeats: this.totalBeats,
4955
+ ...keyed,
4956
+ ...opts
4957
+ };
4958
+ const profile = opts?.timeline === void 0 ? referenceFromReadings(this.#data.notes, this.#chordAnalysis(), asked) : analyzeReference(this.#data.notes, asked);
4959
+ if (opts === void 0) {
4960
+ this.#profile = profile;
4961
+ return copyPlain(profile, "reference profile");
4962
+ }
4963
+ return profile;
4964
+ }
4965
+ /**
4966
+ * The onset placement of the score's melodic line.
4967
+ *
4968
+ * A melody is one line, so a score holding several is read as its top voice,
4969
+ * as {@link Score.motifs} and {@link Score.contour} are.
4970
+ *
4971
+ * @param opts The span's end and the budget; see {@link
4972
+ * RhythmAnalysisOptions}. The meter is the score's own.
4973
+ * @returns The rhythm reading.
4974
+ * @example
4975
+ * ```ts
4976
+ * import { Score } from '@libraz/libcantus';
4977
+ * const score = Score.of([
4978
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
4979
+ * { pitch: 62, startBeat: 1, durationBeat: 1 },
4980
+ * { pitch: 64, startBeat: 2, durationBeat: 1 },
4981
+ * { pitch: 65, startBeat: 3, durationBeat: 1 },
4982
+ * ]);
4983
+ * score.rhythm().onsetDensity; // 4
4984
+ * ```
4985
+ */
4986
+ rhythm(opts) {
4987
+ return analyzeRhythm(this.#data.notes, {
4988
+ meters: this.#data.meters,
4989
+ totalBeats: this.totalBeats,
4990
+ ...opts
4991
+ });
4992
+ }
4993
+ /**
4994
+ * How the score's motifs derive from one another: which statement grew out
4995
+ * of which, and by what transformation.
4996
+ *
4997
+ * {@link Score.motifs} finds the cells; this reads across their occurrences
4998
+ * and names, for every statement after the first, the earlier one it most
4999
+ * plausibly grew out of. A melody is one line, so a score holding several is
5000
+ * read as its top voice, as {@link Score.motifs} is.
5001
+ *
5002
+ * @param opts Cell-length bounds and the recurrence threshold {@link
5003
+ * Score.motifs} takes, plus the variation threshold and the budget; see
5004
+ * {@link MotifGraphOptions} and {@link ExtractMotifsOptions}.
5005
+ * @returns The derivation forest.
5006
+ * @example
5007
+ * ```ts
5008
+ * import { Score } from '@libraz/libcantus';
5009
+ * const score = Score.of([
5010
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
5011
+ * { pitch: 62, startBeat: 1, durationBeat: 1 },
5012
+ * { pitch: 64, startBeat: 2, durationBeat: 1 },
5013
+ * { pitch: 60, startBeat: 3, durationBeat: 1 },
5014
+ * { pitch: 62, startBeat: 4, durationBeat: 1 },
5015
+ * { pitch: 64, startBeat: 5, durationBeat: 1 },
5016
+ * ]);
5017
+ * score.motifGraph().edges.length; // 1
5018
+ * ```
5019
+ */
5020
+ motifGraph(opts) {
5021
+ return motifGraph(this.#data.notes, extractMotifs(this.#data.notes, opts), {
5022
+ key: this.key(),
5023
+ ...opts
5024
+ });
5025
+ }
5026
+ /**
5027
+ * How this score, read as a melody, keeps to a composition plan.
5028
+ *
5029
+ * @param plan The plan the melody was written for.
5030
+ * @param opts The budget; see {@link EvaluateCompositionOptions}.
5031
+ * @returns The violations found and the fit reading; see {@link
5032
+ * evaluateComposition}.
5033
+ * @example
5034
+ * ```ts
5035
+ * import { Composer, Score } from '@libraz/libcantus';
5036
+ * const notes = [60, 62, 64, 65, 67, 65, 64, 62, 60, 62, 64, 65, 67, 65, 64, 60].map(
5037
+ * (pitch, startBeat) => ({ pitch, startBeat, durationBeat: 1 }),
5038
+ * );
5039
+ * const composer = Composer.of({ key: 'C major', seed: 5 });
5040
+ * const plan = composer.plan(Score.of(notes, { key: 'C major' }).reference());
5041
+ * composer.melody(plan).evaluate(plan).violations.some((v) => v.severity === 'error'); // false
5042
+ * ```
5043
+ */
5044
+ evaluate(plan, opts) {
5045
+ return evaluateComposition(this.#data.notes, plan, opts);
5046
+ }
4901
5047
  /**
4902
5048
  * Each note's role in the harmony sounding under it.
4903
5049
  *
@@ -5532,6 +5678,70 @@ var Arrangement = class _Arrangement {
5532
5678
  keys: [...keys]
5533
5679
  });
5534
5680
  }
5681
+ /**
5682
+ * The arrangement's structural fingerprint: form, phrase shape, harmonic
5683
+ * function and rhythm, and how its melody's motifs derive from one another.
5684
+ *
5685
+ * The harmony reads the arrangement's own session analysis directly — the
5686
+ * tracks `settings.harmonyTracks` names, or else every track but the
5687
+ * percussion ones — so the harmony is never inferred a second time. The
5688
+ * melody is the notes of the tracks played with `role: 'melody'`, joined in
5689
+ * the order the tracks were given; an arrangement naming none reads the top
5690
+ * line of the harmony notes instead, as {@link analyzeReference} does
5691
+ * without a `melody` option. Meter, key, harmony and melody are the
5692
+ * arrangement's own and are not on offer here: a caller wanting others calls
5693
+ * {@link analyzeReference} directly, with the same notes {@link
5694
+ * Arrangement.tracks} hands out.
5695
+ *
5696
+ * @param opts The budget; see {@link ReferenceProfileOptions}.
5697
+ * @returns The profile.
5698
+ * @example
5699
+ * ```ts
5700
+ * import { Arrangement } from '@libraz/libcantus';
5701
+ * const triad = (pitches: number[], startBeat: number) =>
5702
+ * pitches.map((pitch) => ({ pitch, startBeat, durationBeat: 4 }));
5703
+ * const arrangement = Arrangement.of(
5704
+ * [
5705
+ * {
5706
+ * name: 'lead',
5707
+ * role: 'melody',
5708
+ * notes: [
5709
+ * ...triad([60, 64, 67], 0),
5710
+ * ...triad([65, 69, 72], 4),
5711
+ * ...triad([67, 71, 74], 8),
5712
+ * ...triad([60, 64, 67], 12),
5713
+ * ],
5714
+ * },
5715
+ * ],
5716
+ * { key: 'C major' },
5717
+ * );
5718
+ * arrangement.reference().harmony.chords.map((chord) => chord.roman); // ['I', 'IV', 'V', 'I']
5719
+ * ```
5720
+ */
5721
+ reference(opts) {
5722
+ const tracks = this.#data.tracks;
5723
+ const harmonyTracks = this.#data.settings?.harmonyTracks;
5724
+ const harmony = [];
5725
+ const melody = [];
5726
+ for (let index = 0; index < tracks.length; index += 1) {
5727
+ const track = tracks[index];
5728
+ if (track === void 0) {
5729
+ continue;
5730
+ }
5731
+ if (track.role !== "drums" && (harmonyTracks === void 0 || harmonyTracks.includes(index))) {
5732
+ harmony.push(...track.notes);
5733
+ }
5734
+ if (track.role === "melody") {
5735
+ melody.push(...track.notes);
5736
+ }
5737
+ }
5738
+ return referenceFromReadings(harmony, this.#session().analysis, {
5739
+ meters: this.#data.settings?.meters,
5740
+ totalBeats: this.timeline().totalBeats,
5741
+ ...melody.length === 0 ? {} : { melody },
5742
+ ...opts
5743
+ });
5744
+ }
5535
5745
  /**
5536
5746
  * Whether another arrangement holds the same tracks under the same settings.
5537
5747
  *
@@ -6228,6 +6438,73 @@ var Composer = class _Composer {
6228
6438
  transposeSemitones: result.transposeSemitones
6229
6439
  };
6230
6440
  }
6441
+ /**
6442
+ * A composition plan derived from a reference profile.
6443
+ *
6444
+ * The composer's key is the plan's home key unless `opts.key` names another,
6445
+ * and its seed and algorithm version are the ones the plan records, so the
6446
+ * plan is written under the same recipe as the composer's other parts.
6447
+ *
6448
+ * @param reference The reference profile to derive from.
6449
+ * @param opts Everything {@link deriveCompositionPlan} takes but the context,
6450
+ * which is the composer's.
6451
+ * @returns The plan.
6452
+ * @throws If the composer draws from a caller-held `rng`: a plan records its
6453
+ * seed, and a source cannot be written into it.
6454
+ * @example
6455
+ * ```ts
6456
+ * import { Composer, Score } from '@libraz/libcantus';
6457
+ * const notes = [60, 62, 64, 65, 67, 65, 64, 62, 60, 62, 64, 65, 67, 65, 64, 60].map(
6458
+ * (pitch, startBeat) => ({ pitch, startBeat, durationBeat: 1 }),
6459
+ * );
6460
+ * const reference = Score.of(notes, { key: 'C major' }).reference();
6461
+ * const plan = Composer.of({ key: 'D major', seed: 5 }).plan(reference);
6462
+ * plan.seed; // 5
6463
+ * plan.keys[0].scale.rootPc; // 2
6464
+ * ```
6465
+ */
6466
+ plan(reference, opts) {
6467
+ const ctx = this.context;
6468
+ if (ctx.rng !== void 0) {
6469
+ throw new InvalidInputError(
6470
+ "composer plan cannot record a caller-held rng; give the composer a seed instead"
6471
+ );
6472
+ }
6473
+ return deriveCompositionPlan(reference, {
6474
+ ...this.#key === void 0 ? {} : { key: this.#key },
6475
+ ...opts,
6476
+ ctx
6477
+ });
6478
+ }
6479
+ /**
6480
+ * A melody written to a composition plan.
6481
+ *
6482
+ * The plan is the reproduction recipe, so its seed and algorithm version are
6483
+ * the ones the melody is drawn under, not the composer's. The score is read
6484
+ * in the plan's meter and home key, at the composer's tempo.
6485
+ *
6486
+ * @param plan The plan to follow.
6487
+ * @param opts The budget; see {@link MelodyOptions}.
6488
+ * @returns The melody, as a score.
6489
+ * @example
6490
+ * ```ts
6491
+ * import { Composer, Score } from '@libraz/libcantus';
6492
+ * const notes = [60, 62, 64, 65, 67, 65, 64, 62, 60, 62, 64, 65, 67, 65, 64, 60].map(
6493
+ * (pitch, startBeat) => ({ pitch, startBeat, durationBeat: 1 }),
6494
+ * );
6495
+ * const composer = Composer.of({ key: 'C major', seed: 5 });
6496
+ * const plan = composer.plan(Score.of(notes, { key: 'C major' }).reference());
6497
+ * composer.melody(plan).notes.length > 0; // true
6498
+ * ```
6499
+ */
6500
+ melody(plan, opts) {
6501
+ const notes = generateMelody(plan, opts);
6502
+ const score = { meters: plan.meters, key: plan.keys[0] };
6503
+ if (this.#options.bpm !== void 0) {
6504
+ score.tempo = this.#options.bpm;
6505
+ }
6506
+ return Score.of(notes, score);
6507
+ }
6231
6508
  /**
6232
6509
  * Whether another composer holds the same settings.
6233
6510
  *
@@ -8106,7 +8383,8 @@ var Voicing = class _Voicing {
8106
8383
  * @param pitch The candidate MIDI pitch.
8107
8384
  * @param query The candidate's harmonic and voice-leading context.
8108
8385
  * @param opts Set `suggestions: false` to skip the search for safe
8109
- * replacements when the verdict is not safe.
8386
+ * replacements when the verdict is not safe, and `partners: true` to learn
8387
+ * which voice of this voicing, by index, raised each pairwise flag.
8110
8388
  * @returns The verdict, reason bitmask, and optional resolution guidance.
8111
8389
  * @throws If the profile is unknown, a value names no chord or key, or a
8112
8390
  * pitch is not a MIDI pitch in 0..127.
@@ -8249,4 +8527,4 @@ export {
8249
8527
  Tuning,
8250
8528
  Voicing
8251
8529
  };
8252
- //# sourceMappingURL=chunk-DKICR7AN.js.map
8530
+ //# sourceMappingURL=chunk-IPEF6NWB.js.map