@libraz/libcantus 0.9.3 → 0.9.4

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 (198) hide show
  1. package/README.md +2 -2
  2. package/dist/analyze/index.cjs +39 -1947
  3. package/dist/analyze/index.cjs.map +1 -1
  4. package/dist/analyze/index.d.cts +197 -37
  5. package/dist/analyze/index.d.ts +197 -37
  6. package/dist/analyze/index.js +19 -8
  7. package/dist/chunk-36N73KZ3.cjs +246 -0
  8. package/dist/chunk-36N73KZ3.cjs.map +1 -0
  9. package/dist/chunk-4HLLZPXT.cjs +3077 -0
  10. package/dist/chunk-4HLLZPXT.cjs.map +1 -0
  11. package/dist/chunk-7MGZ7D62.cjs +498 -0
  12. package/dist/chunk-7MGZ7D62.cjs.map +1 -0
  13. package/dist/{chunk-4MHA432G.js → chunk-7NKDPF74.js} +403 -180
  14. package/dist/chunk-7NKDPF74.js.map +1 -0
  15. package/dist/chunk-CAN76NCU.cjs +679 -0
  16. package/dist/chunk-CAN76NCU.cjs.map +1 -0
  17. package/dist/{chunk-MIWO425M.js → chunk-D2FAJBYL.js} +101 -34
  18. package/dist/chunk-D2FAJBYL.js.map +1 -0
  19. package/dist/{chunk-SGU7SPLK.js → chunk-EHVEBWM5.js} +1056 -531
  20. package/dist/chunk-EHVEBWM5.js.map +1 -0
  21. package/dist/chunk-FCANXBAT.cjs +30 -0
  22. package/dist/chunk-FCANXBAT.cjs.map +1 -0
  23. package/dist/chunk-FNDWXRRI.cjs +479 -0
  24. package/dist/chunk-FNDWXRRI.cjs.map +1 -0
  25. package/dist/{chunk-IT2O4XFE.js → chunk-GQSGS2N4.js} +43 -24
  26. package/dist/chunk-GQSGS2N4.js.map +1 -0
  27. package/dist/{chunk-62QZKCJQ.js → chunk-HKGYCQ34.js} +10 -6
  28. package/dist/chunk-HKGYCQ34.js.map +1 -0
  29. package/dist/{chunk-AKLYPWIN.js → chunk-I24MV65U.js} +29 -6
  30. package/dist/chunk-I24MV65U.js.map +1 -0
  31. package/dist/{chunk-CHMXELDS.js → chunk-KMHOICIY.js} +311 -181
  32. package/dist/chunk-KMHOICIY.js.map +1 -0
  33. package/dist/chunk-LW53AHQU.cjs +1117 -0
  34. package/dist/chunk-LW53AHQU.cjs.map +1 -0
  35. package/dist/{chunk-2ZWMSJH6.js → chunk-NKIBQ6PT.js} +2 -2
  36. package/dist/chunk-NKIBQ6PT.js.map +1 -0
  37. package/dist/chunk-NP4NA2FB.cjs +106 -0
  38. package/dist/chunk-NP4NA2FB.cjs.map +1 -0
  39. package/dist/chunk-NVE34YGZ.cjs +1449 -0
  40. package/dist/chunk-NVE34YGZ.cjs.map +1 -0
  41. package/dist/{chunk-EH6EM37U.js → chunk-NYYWMV2C.js} +61 -49
  42. package/dist/chunk-NYYWMV2C.js.map +1 -0
  43. package/dist/{chunk-GOAW4VYL.js → chunk-Q2XUP227.js} +243 -117
  44. package/dist/chunk-Q2XUP227.js.map +1 -0
  45. package/dist/chunk-Q6OP4U43.cjs +327 -0
  46. package/dist/chunk-Q6OP4U43.cjs.map +1 -0
  47. package/dist/chunk-RC6GBMCD.cjs +845 -0
  48. package/dist/chunk-RC6GBMCD.cjs.map +1 -0
  49. package/dist/{chunk-C5XYKUVG.js → chunk-RK35RIN7.js} +370 -157
  50. package/dist/chunk-RK35RIN7.js.map +1 -0
  51. package/dist/chunk-RUMU3UA6.cjs +53 -0
  52. package/dist/chunk-RUMU3UA6.cjs.map +1 -0
  53. package/dist/chunk-T64C7ZD7.cjs +1 -0
  54. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  55. package/dist/chunk-XEBG3G53.js +498 -0
  56. package/dist/chunk-XEBG3G53.js.map +1 -0
  57. package/dist/{chunk-I5R7ZBJS.js → chunk-YLEMGEFH.js} +103 -59
  58. package/dist/chunk-YLEMGEFH.js.map +1 -0
  59. package/dist/chunk-ZEJR4TKL.cjs +173 -0
  60. package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
  61. package/dist/core/index.cjs +130 -647
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +301 -39
  64. package/dist/core/index.d.ts +301 -39
  65. package/dist/core/index.js +54 -16
  66. package/dist/{function-DYt4iKsn.d.cts → function-BtiZb9i9.d.ts} +28 -7
  67. package/dist/{function-DVH4Eusr.d.ts → function-DNugAOrt.d.cts} +28 -7
  68. package/dist/generate/index.cjs +28 -4085
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +370 -62
  71. package/dist/generate/index.d.ts +370 -62
  72. package/dist/generate/index.js +23 -9
  73. package/dist/{index-CxqBQtH1.d.cts → index-6VR8-kit.d.ts} +19 -4
  74. package/dist/index-70p168VM.d.ts +192 -0
  75. package/dist/{index-CpOsP1y9.d.cts → index-Be74mcon.d.cts} +18 -17
  76. package/dist/index-BtXT6UQ0.d.cts +192 -0
  77. package/dist/{index-DxvARIP2.d.ts → index-C5UBbQ4G.d.ts} +18 -17
  78. package/dist/{index-Bt4tOUD9.d.ts → index-C83mZI1P.d.ts} +93 -8
  79. package/dist/{index-Bg5dYMmZ.d.ts → index-D6b6na69.d.cts} +14 -11
  80. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  81. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  82. package/dist/index-OG65ocOr.d.cts +272 -0
  83. package/dist/index-OG65ocOr.d.ts +272 -0
  84. package/dist/{index-Jc0Ryi79.d.ts → index-tJhkmQgE.d.cts} +19 -4
  85. package/dist/{index-Dikst44v.d.cts → index-tSQh-xwY.d.ts} +14 -11
  86. package/dist/{index-BNlGP86A.d.cts → index-y_ak9_Hi.d.cts} +93 -8
  87. package/dist/index.cjs +419 -7383
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.d.cts +14 -14
  90. package/dist/index.d.ts +14 -14
  91. package/dist/index.js +111 -27
  92. package/dist/model/index.cjs +8 -2562
  93. package/dist/model/index.cjs.map +1 -1
  94. package/dist/model/index.d.cts +433 -75
  95. package/dist/model/index.d.ts +433 -75
  96. package/dist/model/index.js +6 -7
  97. package/dist/styled-DIRkk9Uz.d.ts +506 -0
  98. package/dist/styled-DJbkJQfj.d.cts +506 -0
  99. package/dist/theory/index.cjs +172 -1822
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +151 -233
  102. package/dist/theory/index.d.ts +151 -233
  103. package/dist/theory/index.js +30 -7
  104. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  105. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  106. package/package.json +24 -6
  107. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  108. package/dist/chunk-4MHA432G.js.map +0 -1
  109. package/dist/chunk-62QZKCJQ.js.map +0 -1
  110. package/dist/chunk-AKLYPWIN.js.map +0 -1
  111. package/dist/chunk-C5XYKUVG.js.map +0 -1
  112. package/dist/chunk-CHMXELDS.js.map +0 -1
  113. package/dist/chunk-DYWFNC4A.js +0 -152
  114. package/dist/chunk-DYWFNC4A.js.map +0 -1
  115. package/dist/chunk-EH6EM37U.js.map +0 -1
  116. package/dist/chunk-FBQK3ZQN.js +0 -131
  117. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  118. package/dist/chunk-GOAW4VYL.js.map +0 -1
  119. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  120. package/dist/chunk-IT2O4XFE.js.map +0 -1
  121. package/dist/chunk-MIWO425M.js.map +0 -1
  122. package/dist/chunk-SGU7SPLK.js.map +0 -1
  123. package/dist/index-CGMZNCx6.d.cts +0 -140
  124. package/dist/index-CGMZNCx6.d.ts +0 -140
  125. package/dist/index-CnZCo29u.d.cts +0 -108
  126. package/dist/index-JnF4v9bx.d.ts +0 -108
  127. package/dist/styled-DZ7GjShb.d.ts +0 -282
  128. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  129. package/src/analyze/arrange/index.ts +0 -21
  130. package/src/analyze/arrange/internal.ts +0 -127
  131. package/src/analyze/arrange/tension.ts +0 -199
  132. package/src/analyze/arrange/tracks.ts +0 -312
  133. package/src/analyze/detect/index.ts +0 -272
  134. package/src/analyze/functional/borrowed.ts +0 -84
  135. package/src/analyze/functional/cadence.ts +0 -66
  136. package/src/analyze/functional/function.ts +0 -184
  137. package/src/analyze/functional/index.ts +0 -23
  138. package/src/analyze/functional/internal.ts +0 -29
  139. package/src/analyze/functional/roman.ts +0 -402
  140. package/src/analyze/index.ts +0 -49
  141. package/src/analyze/timeline/index.ts +0 -451
  142. package/src/analyze/voice/index.ts +0 -339
  143. package/src/core/event-index/index.ts +0 -104
  144. package/src/core/index.ts +0 -61
  145. package/src/core/interval/index.ts +0 -81
  146. package/src/core/meter/index.ts +0 -302
  147. package/src/core/pitch/index.ts +0 -311
  148. package/src/core/random/index.ts +0 -76
  149. package/src/core/tuning/index.ts +0 -186
  150. package/src/core/types.ts +0 -29
  151. package/src/core/validation/index.ts +0 -119
  152. package/src/generate/bass/index.ts +0 -373
  153. package/src/generate/countermelody/index.ts +0 -470
  154. package/src/generate/drums/beat.ts +0 -266
  155. package/src/generate/drums/euclid.ts +0 -65
  156. package/src/generate/drums/fills.ts +0 -356
  157. package/src/generate/drums/ghost.ts +0 -119
  158. package/src/generate/drums/hihat.ts +0 -285
  159. package/src/generate/drums/hit.ts +0 -31
  160. package/src/generate/drums/index.ts +0 -361
  161. package/src/generate/drums/internal.ts +0 -257
  162. package/src/generate/drums/kick.ts +0 -160
  163. package/src/generate/drums/percussion.ts +0 -121
  164. package/src/generate/drums/rng.ts +0 -7
  165. package/src/generate/drums/swing.ts +0 -71
  166. package/src/generate/groove/index.ts +0 -351
  167. package/src/generate/harmonize/index.ts +0 -451
  168. package/src/generate/index.ts +0 -58
  169. package/src/generate/motif/index.ts +0 -422
  170. package/src/generate/progression/index.ts +0 -362
  171. package/src/generate/reharmony/index.ts +0 -323
  172. package/src/generate/rhythm/index.ts +0 -175
  173. package/src/index.ts +0 -13
  174. package/src/model/chord.ts +0 -442
  175. package/src/model/index.ts +0 -15
  176. package/src/model/interval.ts +0 -82
  177. package/src/model/key.ts +0 -266
  178. package/src/model/note.ts +0 -176
  179. package/src/model/progression.ts +0 -161
  180. package/src/model/shared.ts +0 -12
  181. package/src/theory/chord/index.ts +0 -375
  182. package/src/theory/chordscale/index.ts +0 -374
  183. package/src/theory/counterpoint/index.ts +0 -230
  184. package/src/theory/harmony/index.ts +0 -77
  185. package/src/theory/index.ts +0 -112
  186. package/src/theory/safety/index.ts +0 -488
  187. package/src/theory/scale/degrees.ts +0 -123
  188. package/src/theory/scale/index.ts +0 -28
  189. package/src/theory/scale/key.ts +0 -56
  190. package/src/theory/scale/masks.ts +0 -153
  191. package/src/theory/spelling/index.ts +0 -249
  192. package/src/theory/symbol/index.ts +0 -268
  193. package/src/theory/voicing/index.ts +0 -5
  194. package/src/theory/voicing/internal.ts +0 -168
  195. package/src/theory/voicing/leading.ts +0 -112
  196. package/src/theory/voicing/satb.ts +0 -216
  197. package/src/theory/voicing/styled.ts +0 -199
  198. package/tsconfig.json +0 -23
@@ -0,0 +1,845 @@
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
+
3
+
4
+
5
+
6
+
7
+ var _chunk36N73KZ3cjs = require('./chunk-36N73KZ3.cjs');
8
+
9
+
10
+
11
+
12
+
13
+
14
+ var _chunkCAN76NCUcjs = require('./chunk-CAN76NCU.cjs');
15
+
16
+
17
+ var _chunkQ6OP4U43cjs = require('./chunk-Q6OP4U43.cjs');
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+ var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ var _chunk7MGZ7D62cjs = require('./chunk-7MGZ7D62.cjs');
38
+
39
+ // src/analyze/timeline/index.ts
40
+ function chordTimelineFromChords(chords, totalBeats) {
41
+ _chunk7MGZ7D62cjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
42
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, chords.length, "timeline chords");
43
+ for (let index = 0; index < chords.length; index += 1) {
44
+ _chunk7MGZ7D62cjs.assertRange.call(void 0,
45
+ _nullishCoalesce(_optionalChain([chords, 'access', _2 => _2[index], 'optionalAccess', _3 => _3.startBeat]), () => ( Number.NaN)),
46
+ 0,
47
+ Number.MAX_SAFE_INTEGER,
48
+ `chords[${index}].startBeat`
49
+ );
50
+ }
51
+ const sorted = [...chords].filter((chord) => chord.startBeat < totalBeats).sort((a, b) => a.startBeat - b.startBeat);
52
+ const segments = sorted.map((gc, i) => {
53
+ const next = sorted[i + 1];
54
+ const nextBeat = next ? next.startBeat : totalBeats;
55
+ const endBeat = Math.min(totalBeats, Math.max(gc.startBeat, nextBeat));
56
+ return {
57
+ startBeat: gc.startBeat,
58
+ endBeat,
59
+ chord: _chunkFNDWXRRIcjs.makeChord.call(void 0, gc.rootPc, gc.quality, gc.bassPc)
60
+ };
61
+ }).filter((seg) => seg.endBeat > seg.startBeat);
62
+ return { at: segmentLookup(segments), segments };
63
+ }
64
+ function segmentLookup(segments) {
65
+ return (beat) => {
66
+ _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, beat, "timeline query beat");
67
+ let low = 0;
68
+ let high = segments.length;
69
+ while (low < high) {
70
+ const middle = Math.floor((low + high) / 2);
71
+ if ((_nullishCoalesce(_optionalChain([segments, 'access', _4 => _4[middle], 'optionalAccess', _5 => _5.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
72
+ low = middle + 1;
73
+ } else {
74
+ high = middle;
75
+ }
76
+ }
77
+ const candidate = segments[low - 1];
78
+ return candidate !== void 0 && beat >= candidate.startBeat && beat < candidate.endBeat ? candidate.chord : null;
79
+ };
80
+ }
81
+ var EPS = 1e-9;
82
+ var NOISE_THRESHOLD_RATIO = 0.2;
83
+ var MAX_DETECTION_PCS = 6;
84
+ var FALLBACK_PCS = 3;
85
+ var INEXACT_CONFIDENCE_FACTOR = 0.85;
86
+ var DIATONIC_BONUS = 0.5;
87
+ var EXACT_BONUS = 0.5;
88
+ var MISMATCH_PENALTY = 0.3;
89
+ function scoreMatch(match, weights, maxWeight, key) {
90
+ const rootWeight = maxWeight > 0 ? (_nullishCoalesce(weights[match.rootPc], () => ( 0))) / maxWeight : 0;
91
+ const tones = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, _chunkFNDWXRRIcjs.makeChord.call(void 0, match.rootPc, match.quality));
92
+ let score = rootWeight;
93
+ if (tones.every((pc) => _chunkFNDWXRRIcjs.isScaleTone.call(void 0, pc, key))) {
94
+ score += DIATONIC_BONUS;
95
+ }
96
+ if (match.exact) {
97
+ score += EXACT_BONUS;
98
+ }
99
+ score -= MISMATCH_PENALTY * (match.extraPcs.length + match.missingPcs.length);
100
+ return score;
101
+ }
102
+ function chordConfidence(chord, weights, totalWeight, exact) {
103
+ const tones = new Set(_chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord));
104
+ let chordWeight = 0;
105
+ for (let pc = 0; pc < 12; pc += 1) {
106
+ if (tones.has(pc)) {
107
+ chordWeight += _nullishCoalesce(weights[pc], () => ( 0));
108
+ }
109
+ }
110
+ const raw = totalWeight > 0 ? chordWeight / totalWeight : 0;
111
+ const confidence = Math.min(1, Math.max(0, raw));
112
+ return exact ? confidence : confidence * INEXACT_CONFIDENCE_FACTOR;
113
+ }
114
+ function analyzeWindow(notes, windowStart, windowEnd, ts, key) {
115
+ const weights = new Array(12).fill(0);
116
+ let lowestPitch = Number.POSITIVE_INFINITY;
117
+ for (const note of notes) {
118
+ const noteEnd = note.startBeat + note.durationBeat;
119
+ const overlap = Math.min(noteEnd, windowEnd) - Math.max(note.startBeat, windowStart);
120
+ if (overlap <= EPS) {
121
+ continue;
122
+ }
123
+ const velocityFactor = note.velocity !== void 0 ? note.velocity / 127 : 1;
124
+ const onsetInWindow = note.startBeat >= windowStart - EPS && note.startBeat < windowEnd - EPS;
125
+ const accent = onsetInWindow ? 1 + _chunk36N73KZ3cjs.metricWeight.call(void 0, note.startBeat, ts) / 3 : 1;
126
+ const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, note.pitch);
127
+ weights[pc] = (_nullishCoalesce(weights[pc], () => ( 0))) + overlap * velocityFactor * accent;
128
+ if (note.pitch < lowestPitch) {
129
+ lowestPitch = note.pitch;
130
+ }
131
+ }
132
+ let totalWeight = 0;
133
+ let maxWeight = 0;
134
+ for (const w of weights) {
135
+ totalWeight += w;
136
+ maxWeight = Math.max(maxWeight, w);
137
+ }
138
+ if (totalWeight <= EPS) {
139
+ return null;
140
+ }
141
+ const ranked = weights.map((weight, pc) => ({ pc, weight })).filter(({ weight }) => weight > 0).sort((a, b) => b.weight - a.weight || a.pc - b.pc);
142
+ const selected = ranked.filter(({ weight }) => weight >= maxWeight * NOISE_THRESHOLD_RATIO).slice(0, MAX_DETECTION_PCS).map(({ pc }) => pc);
143
+ const bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, lowestPitch);
144
+ const anchorPc = selected.includes(bassPc) ? bassPc : _nullishCoalesce(selected[0], () => ( bassPc));
145
+ const detectionPitches = selected.map((pc) => pc === anchorPc ? pc : pc + 12);
146
+ const matches = _chunkCAN76NCUcjs.detectChord.call(void 0, detectionPitches);
147
+ let bestMatch = null;
148
+ let bestScore = Number.NEGATIVE_INFINITY;
149
+ for (const match of matches) {
150
+ const score = scoreMatch(match, weights, maxWeight, key);
151
+ if (score > bestScore) {
152
+ bestScore = score;
153
+ bestMatch = match;
154
+ }
155
+ }
156
+ if (bestMatch) {
157
+ const chord = _chunkFNDWXRRIcjs.makeChord.call(void 0, bestMatch.rootPc, bestMatch.quality, bestMatch.bassPc);
158
+ return { chord, confidence: chordConfidence(chord, weights, totalWeight, bestMatch.exact) };
159
+ }
160
+ const fallback = _chunkCAN76NCUcjs.detectChordBest.call(void 0, selected.slice(0, FALLBACK_PCS));
161
+ if (fallback) {
162
+ return { chord: fallback, confidence: chordConfidence(fallback, weights, totalWeight, false) };
163
+ }
164
+ return null;
165
+ }
166
+ function sameChord(a, b) {
167
+ return a.rootPc === b.rootPc && a.quality === b.quality && a.bassPc === b.bassPc;
168
+ }
169
+ function chordTimelineFromNotes(notes, opts = {}) {
170
+ const ts = _nullishCoalesce(opts.ts, () => ( _chunk36N73KZ3cjs.parseTimeSignature.call(void 0, "4/4")));
171
+ _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
172
+ const harmonicRhythm = _nullishCoalesce(opts.harmonicRhythm, () => ( _chunk36N73KZ3cjs.beatsPerBar.call(void 0, ts)));
173
+ _chunk7MGZ7D62cjs.assertRange.call(void 0, harmonicRhythm, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "harmonic rhythm");
174
+ const budget = opts.budget;
175
+ _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, notes, "timeline notes", { allowNonPositiveDuration: true, budget });
176
+ const soundingIndex = _chunk36N73KZ3cjs.createNoteEventIndex.call(void 0,
177
+ notes.filter((note) => note.durationBeat > 0),
178
+ { name: "timeline notes", budget }
179
+ );
180
+ const sounding = soundingIndex.notes.map(({ note }) => note);
181
+ const lastNoteEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);
182
+ const totalBeats = _nullishCoalesce(opts.totalBeats, () => ( lastNoteEnd));
183
+ _chunk7MGZ7D62cjs.assertRange.call(void 0, totalBeats, 0, Number.MAX_SAFE_INTEGER, "timeline totalBeats");
184
+ const key = _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkCAN76NCUcjs.detectKeyFromNotes.call(void 0, sounding), 'access', _6 => _6[0], 'optionalAccess', _7 => _7.key]))), () => ( _chunkFNDWXRRIcjs.majorKey.call(void 0, 0)));
185
+ const segments = [];
186
+ const segmentConfidence = [];
187
+ const windowCount = Math.max(0, Math.ceil(totalBeats / harmonicRhythm - EPS));
188
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, windowCount, "timeline windows", budget);
189
+ const windowNotes = Array.from({ length: windowCount }, () => []);
190
+ let memberships = 0;
191
+ for (const indexed of soundingIndex.notes) {
192
+ const first = Math.max(0, Math.floor(indexed.note.startBeat / harmonicRhythm));
193
+ const lastExclusive = Math.min(windowCount, Math.ceil(indexed.endBeat / harmonicRhythm));
194
+ memberships += Math.max(0, lastExclusive - first);
195
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, memberships, "timeline note-to-window memberships", budget);
196
+ for (let window = first; window < lastExclusive; window += 1) {
197
+ _optionalChain([windowNotes, 'access', _8 => _8[window], 'optionalAccess', _9 => _9.push, 'call', _10 => _10(indexed.note)]);
198
+ }
199
+ }
200
+ for (let i = 0; i < windowCount; i += 1) {
201
+ const start = i * harmonicRhythm;
202
+ const end = Math.min(start + harmonicRhythm, totalBeats);
203
+ const inferred = analyzeWindow(_nullishCoalesce(windowNotes[i], () => ( [])), start, end, ts, key);
204
+ if (!inferred) {
205
+ continue;
206
+ }
207
+ const last = segments[segments.length - 1];
208
+ if (last && sameChord(last.chord, inferred.chord) && Math.abs(last.endBeat - start) < EPS) {
209
+ const lastLength = last.endBeat - last.startBeat;
210
+ const length = end - start;
211
+ const lastConfidence = _nullishCoalesce(segmentConfidence[segmentConfidence.length - 1], () => ( 0));
212
+ segmentConfidence[segmentConfidence.length - 1] = (lastConfidence * lastLength + inferred.confidence * length) / (lastLength + length);
213
+ last.endBeat = end;
214
+ } else {
215
+ segments.push({ startBeat: start, endBeat: end, chord: inferred.chord });
216
+ segmentConfidence.push(inferred.confidence);
217
+ }
218
+ }
219
+ return {
220
+ timeline: { at: segmentLookup(segments), segments },
221
+ key,
222
+ segmentConfidence
223
+ };
224
+ }
225
+ function detectCadences(timeline, key) {
226
+ const hits = [];
227
+ for (let i = 1; i < timeline.segments.length; i += 1) {
228
+ const prev = timeline.segments[i - 1];
229
+ const cur = timeline.segments[i];
230
+ if (!prev || !cur) {
231
+ continue;
232
+ }
233
+ if (Math.abs(cur.startBeat - prev.endBeat) > EPS) {
234
+ continue;
235
+ }
236
+ const type = _chunkCAN76NCUcjs.detectCadence.call(void 0, prev.chord, cur.chord, key);
237
+ if (type !== null) {
238
+ hits.push({ atBeat: cur.startBeat, type, from: prev.chord, to: cur.chord });
239
+ }
240
+ }
241
+ return hits;
242
+ }
243
+
244
+ // src/analyze/arrange/internal.ts
245
+ var EPS2 = 1e-9;
246
+ var MAX_LANE_LEAP = 24;
247
+ function covers(note, beat) {
248
+ return beat >= note.startBeat - EPS2 && beat < note.endBeat - EPS2;
249
+ }
250
+ function isPercussion(role) {
251
+ return role === "drums";
252
+ }
253
+ function poolNotes(tracks, only) {
254
+ const notes = [];
255
+ for (let index = 0; index < tracks.length; index += 1) {
256
+ const track = tracks[index];
257
+ if (track === void 0 || isPercussion(track.role)) {
258
+ continue;
259
+ }
260
+ if (only !== void 0 && !only.has(index)) {
261
+ continue;
262
+ }
263
+ for (const note of track.notes) {
264
+ if (note.durationBeat > 0) {
265
+ notes.push(note);
266
+ }
267
+ }
268
+ }
269
+ return notes;
270
+ }
271
+ function splitIntoSubVoices(ordered) {
272
+ const lanes = [];
273
+ for (const note of ordered) {
274
+ let best = -1;
275
+ let bestDistance = Number.POSITIVE_INFINITY;
276
+ let bestEnd = Number.NEGATIVE_INFINITY;
277
+ for (let lane = 0; lane < lanes.length; lane += 1) {
278
+ const last = _optionalChain([lanes, 'access', _11 => _11[lane], 'optionalAccess', _12 => _12[(_nullishCoalesce(_optionalChain([lanes, 'access', _13 => _13[lane], 'optionalAccess', _14 => _14.length]), () => ( 0))) - 1]]);
279
+ if (last === void 0) {
280
+ continue;
281
+ }
282
+ const end = last.startBeat + last.durationBeat;
283
+ if (end > note.startBeat + EPS2) {
284
+ continue;
285
+ }
286
+ const distance = Math.abs(last.pitch - note.pitch);
287
+ if (distance > MAX_LANE_LEAP) {
288
+ continue;
289
+ }
290
+ if (distance < bestDistance || distance === bestDistance && end > bestEnd) {
291
+ best = lane;
292
+ bestDistance = distance;
293
+ bestEnd = end;
294
+ }
295
+ }
296
+ if (best >= 0) {
297
+ _optionalChain([lanes, 'access', _15 => _15[best], 'optionalAccess', _16 => _16.push, 'call', _17 => _17(note)]);
298
+ } else {
299
+ lanes.push([note]);
300
+ }
301
+ }
302
+ return lanes;
303
+ }
304
+ function lastStartingAtOrBefore(sounding, beat) {
305
+ let low = 0;
306
+ let high = sounding.length;
307
+ while (low < high) {
308
+ const middle = Math.floor((low + high) / 2);
309
+ if ((_nullishCoalesce(_optionalChain([sounding, 'access', _18 => _18[middle], 'optionalAccess', _19 => _19.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + EPS2) {
310
+ low = middle + 1;
311
+ } else {
312
+ high = middle;
313
+ }
314
+ }
315
+ return low - 1;
316
+ }
317
+ function prepareTracks(tracks) {
318
+ let nextId = 0;
319
+ return tracks.map((track, index) => {
320
+ const ordered = track.notes.map((note, originalIndex) => ({ note, originalIndex })).filter(({ note }) => note.durationBeat > 0).sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch).map(({ note, originalIndex }) => ({ ...note, id: nextId++, originalIndex }));
321
+ const voices = splitIntoSubVoices(ordered).map((voice) => {
322
+ const sounding = voice.map((note, i) => {
323
+ const prev = i > 0 ? voice[i - 1] : void 0;
324
+ const prevEnd = prev === void 0 ? void 0 : prev.startBeat + prev.durationBeat;
325
+ const contiguous = prevEnd !== void 0 && Math.abs(prevEnd - note.startBeat) <= EPS2;
326
+ return {
327
+ pitch: note.pitch,
328
+ prevPitch: contiguous ? _optionalChain([prev, 'optionalAccess', _20 => _20.pitch]) : void 0,
329
+ startBeat: note.startBeat,
330
+ endBeat: note.startBeat + note.durationBeat
331
+ };
332
+ });
333
+ return {
334
+ voice,
335
+ sounding,
336
+ at: (beat) => {
337
+ const candidate = sounding[lastStartingAtOrBefore(sounding, beat)];
338
+ return candidate !== void 0 && covers(candidate, beat) ? candidate : void 0;
339
+ }
340
+ };
341
+ });
342
+ return {
343
+ name: _nullishCoalesce(track.name, () => ( `track ${index + 1}`)),
344
+ role: _nullishCoalesce(track.role, () => ( "other")),
345
+ trackIndex: index,
346
+ voices
347
+ };
348
+ });
349
+ }
350
+
351
+ // src/analyze/arrange/tension.ts
352
+ var FUNCTION_TENSION = {
353
+ tonic: 0,
354
+ subdominant: 0.5,
355
+ dominant: 1
356
+ };
357
+ var FUNCTION_WEIGHT = 0.5;
358
+ var DISSONANCE_WEIGHT = 0.35;
359
+ var SPAN_WEIGHT = 0.15;
360
+ var SPAN_SATURATION = 24;
361
+ function clamp01(value) {
362
+ return Math.min(1, Math.max(0, value));
363
+ }
364
+ function tensionCurve(tracks, opts = {}) {
365
+ const ts = _nullishCoalesce(opts.ts, () => ( _chunk36N73KZ3cjs.parseTimeSignature.call(void 0, "4/4")));
366
+ _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
367
+ const budget = opts.budget;
368
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
369
+ for (let index = 0; index < tracks.length; index += 1) {
370
+ _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, _nullishCoalesce(_optionalChain([tracks, 'access', _21 => _21[index], 'optionalAccess', _22 => _22.notes]), () => ( [])), `tracks[${index}].notes`, {
371
+ allowNonPositiveDuration: true,
372
+ budget
373
+ });
374
+ }
375
+ const profile = _nullishCoalesce(opts.profile, () => ( "pop"));
376
+ const step = _nullishCoalesce(opts.step, () => ( 1));
377
+ _chunk7MGZ7D62cjs.assertRange.call(void 0, step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, "tension sampling step");
378
+ const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
379
+ const pooled = poolNotes(tracks, harmonyTracks);
380
+ const totalBeats = poolNotes(tracks).reduce(
381
+ (end, n) => Math.max(end, n.startBeat + n.durationBeat),
382
+ 0
383
+ );
384
+ const { timeline, key } = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
385
+ key: opts.key,
386
+ ts,
387
+ harmonicRhythm: opts.harmonicRhythm,
388
+ budget
389
+ }) : {
390
+ timeline: opts.timeline,
391
+ key: _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkCAN76NCUcjs.detectKeyFromNotes.call(void 0, pooled), 'access', _23 => _23[0], 'optionalAccess', _24 => _24.key]))), () => ( _chunkFNDWXRRIcjs.majorKey.call(void 0, 0)))
392
+ };
393
+ const prepared = prepareTracks(tracks);
394
+ const points = [];
395
+ const sampleCount = Math.max(0, Math.ceil(totalBeats / step - EPS2));
396
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, sampleCount, "tension samples", budget);
397
+ const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);
398
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, sampleCount * voiceCount, "tension sample-voice lookups", budget);
399
+ for (let i = 0; i < sampleCount; i += 1) {
400
+ const beat = i * step;
401
+ points.push({ beat, tension: sampleTension(prepared, timeline, key, ts, profile, beat) });
402
+ }
403
+ return points;
404
+ }
405
+ function tensionCurveFrom(tracks, analysis, opts = {}) {
406
+ return tensionCurve(tracks, { ...opts, timeline: analysis.timeline, key: analysis.key });
407
+ }
408
+ function allSounding(prepared, beat) {
409
+ const out = [];
410
+ for (let t = 0; t < prepared.length; t += 1) {
411
+ const track = prepared[t];
412
+ if (!track || isPercussion(track.role)) {
413
+ continue;
414
+ }
415
+ for (const subVoice of track.voices) {
416
+ const note = subVoice.at(beat);
417
+ if (note !== void 0) {
418
+ out.push({ pitch: note.pitch, track: t });
419
+ }
420
+ }
421
+ }
422
+ return out;
423
+ }
424
+ function sampleTension(prepared, timeline, key, ts, profile, beat) {
425
+ const sounding = allSounding(prepared, beat);
426
+ if (sounding.length === 0) {
427
+ return 0;
428
+ }
429
+ const chord = timeline.at(beat);
430
+ const functionScore = chord ? FUNCTION_TENSION[_chunkCAN76NCUcjs.functionOf.call(void 0, chord, key)] : 0;
431
+ const chordPcs = chord ? new Set(_chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord)) : null;
432
+ const strongBeat = _chunk36N73KZ3cjs.isStrongBeat.call(void 0, beat, ts);
433
+ let nonChord = 0;
434
+ let dissonant = 0;
435
+ for (let i = 0; i < sounding.length; i += 1) {
436
+ const voice = sounding[i];
437
+ if (!voice) {
438
+ continue;
439
+ }
440
+ if (chordPcs && !chordPcs.has(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, voice.pitch))) {
441
+ nonChord += 1;
442
+ }
443
+ const others = sounding.filter((_, j) => j !== i).map((s) => ({ pitch: s.pitch }));
444
+ const result = _chunkQ6OP4U43cjs.evaluateSafety.call(void 0,
445
+ {
446
+ profile,
447
+ candidatePitch: voice.pitch,
448
+ chord,
449
+ key,
450
+ otherVoices: others,
451
+ strongBeat
452
+ },
453
+ { suggestions: false }
454
+ );
455
+ if (result.safety === 2 /* Dissonant */) {
456
+ dissonant += 1;
457
+ }
458
+ }
459
+ const dissonanceScore = Math.max(nonChord, dissonant) / sounding.length;
460
+ let low = Number.POSITIVE_INFINITY;
461
+ let high = Number.NEGATIVE_INFINITY;
462
+ for (const voice of sounding) {
463
+ low = Math.min(low, voice.pitch);
464
+ high = Math.max(high, voice.pitch);
465
+ }
466
+ const spanScore = Math.min(1, (high - low) / SPAN_SATURATION);
467
+ return clamp01(
468
+ FUNCTION_WEIGHT * functionScore + DISSONANCE_WEIGHT * dissonanceScore + SPAN_WEIGHT * spanScore
469
+ );
470
+ }
471
+
472
+ // src/analyze/voice/index.ts
473
+ function toVoiceNotes(events) {
474
+ return events.map((event, index) => ({ ...event, id: index, originalIndex: index }));
475
+ }
476
+ var EPS3 = 1e-9;
477
+ function intervalAboveBass(pitch, others, chord) {
478
+ let bass = Number.POSITIVE_INFINITY;
479
+ for (const ov of others) {
480
+ bass = Math.min(bass, ov.pitch);
481
+ }
482
+ if (Number.isFinite(bass) && bass < pitch) {
483
+ return ((pitch - bass) % 12 + 12) % 12;
484
+ }
485
+ const ref = _nullishCoalesce(chord.bassPc, () => ( chord.rootPc));
486
+ return ((_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch) - _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, ref)) % 12 + 12) % 12;
487
+ }
488
+ function tensionDegree(ic) {
489
+ if (ic === 1 || ic === 2) {
490
+ return 9;
491
+ }
492
+ if (ic === 5 || ic === 6) {
493
+ return 11;
494
+ }
495
+ return 13;
496
+ }
497
+ function isStep(a, b) {
498
+ const d = Math.abs(a - b);
499
+ return d === 1 || d === 2;
500
+ }
501
+ function suspensionType(ic, delta) {
502
+ if (delta > 0) {
503
+ return "sus2-3";
504
+ }
505
+ if (ic === 2) {
506
+ return "sus9-8";
507
+ }
508
+ if (ic === 10 || ic === 11) {
509
+ return "sus7-6";
510
+ }
511
+ if (ic === 5 || ic === 6) {
512
+ return "sus4-3";
513
+ }
514
+ if (ic === 8 || ic === 9) {
515
+ return "sus6-5";
516
+ }
517
+ return null;
518
+ }
519
+ function stepResolution(pitch, chord) {
520
+ for (let delta = 1; delta <= 2; delta += 1) {
521
+ if (_chunkFNDWXRRIcjs.isChordMember.call(void 0, pitch - delta, chord)) {
522
+ return pitch - delta;
523
+ }
524
+ }
525
+ return void 0;
526
+ }
527
+ function analyzeVoice(voice, chordAtBeat, key, otherVoicesAtBeat = () => []) {
528
+ _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, voice, "voice notes", { allowNonPositiveDuration: true });
529
+ const result = [];
530
+ for (let i = 0; i < voice.length; i += 1) {
531
+ const note = voice[i];
532
+ if (!note) {
533
+ continue;
534
+ }
535
+ const prevNote = i > 0 ? voice[i - 1] : void 0;
536
+ const nextNote = i + 1 < voice.length ? voice[i + 1] : void 0;
537
+ const prevEnd = prevNote === void 0 ? Number.NEGATIVE_INFINITY : prevNote.startBeat + prevNote.durationBeat;
538
+ const noteEnd = note.startBeat + note.durationBeat;
539
+ const prev = prevNote !== void 0 && Math.abs(prevEnd - note.startBeat) <= EPS3 ? prevNote : void 0;
540
+ const next = nextNote !== void 0 && Math.abs(noteEnd - nextNote.startBeat) <= EPS3 ? nextNote : void 0;
541
+ const chord = chordAtBeat(note.startBeat);
542
+ const labels = [];
543
+ const member = _chunkFNDWXRRIcjs.isChordMember.call(void 0, note.pitch, chord);
544
+ let handled = false;
545
+ if (chord && member) {
546
+ const role = _chunkFNDWXRRIcjs.chordToneRole.call(void 0, note.pitch, chord);
547
+ if (role) {
548
+ labels.push({ kind: "chordTone", role });
549
+ } else {
550
+ const ic = _chunkFNDWXRRIcjs.intervalAboveRoot.call(void 0, note.pitch, chord);
551
+ labels.push({ kind: "tension", degree: tensionDegree(ic) });
552
+ }
553
+ handled = true;
554
+ }
555
+ if (!handled && chord && prev && next) {
556
+ const prevChord = chordAtBeat(prev.startBeat);
557
+ const prepared = prev.pitch === note.pitch && _chunkFNDWXRRIcjs.isChordMember.call(void 0, prev.pitch, prevChord);
558
+ const resolves = isStep(next.pitch, note.pitch);
559
+ const others = otherVoicesAtBeat(note.startBeat);
560
+ const verticallyDissonant = others.some(
561
+ (ov) => _chunkFNDWXRRIcjs.createsVerticalDissonance.call(void 0, note.pitch, ov.pitch, true)
562
+ );
563
+ if (prepared && verticallyDissonant && resolves) {
564
+ const ic = intervalAboveBass(note.pitch, others, chord);
565
+ const type = suspensionType(ic, next.pitch - note.pitch);
566
+ if (type !== null) {
567
+ labels.push({ kind: "suspension", type, resolveTo: next.pitch });
568
+ handled = true;
569
+ }
570
+ }
571
+ }
572
+ if (!handled && chord && prev && next) {
573
+ const prevMember = _chunkFNDWXRRIcjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat));
574
+ const nextMember = _chunkFNDWXRRIcjs.isChordMember.call(void 0, next.pitch, chordAtBeat(next.startBeat));
575
+ const up = note.pitch - prev.pitch > 0;
576
+ const contInto = next.pitch - note.pitch > 0;
577
+ const sameDir = note.pitch !== prev.pitch && next.pitch !== note.pitch && up === contInto;
578
+ if (prevMember && nextMember && !member && isStep(note.pitch, prev.pitch) && isStep(next.pitch, note.pitch) && sameDir) {
579
+ labels.push({ kind: "passing" });
580
+ handled = true;
581
+ }
582
+ }
583
+ if (!handled && chord && prev && next && !member && prev.pitch === next.pitch && _chunkFNDWXRRIcjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat)) && isStep(note.pitch, prev.pitch)) {
584
+ labels.push({ kind: "neighbor" });
585
+ handled = true;
586
+ }
587
+ if (!handled && chord && next && !member && note.pitch === next.pitch) {
588
+ const nextChord = chordAtBeat(next.startBeat);
589
+ if (_chunkFNDWXRRIcjs.isChordMember.call(void 0, next.pitch, nextChord)) {
590
+ labels.push({ kind: "anticipation" });
591
+ handled = true;
592
+ }
593
+ }
594
+ if (!handled && chord && prev && next && !member) {
595
+ const prevMember = _chunkFNDWXRRIcjs.isChordMember.call(void 0, prev.pitch, chordAtBeat(prev.startBeat));
596
+ const nextMember = _chunkFNDWXRRIcjs.isChordMember.call(void 0, next.pitch, chordAtBeat(next.startBeat));
597
+ const stepFromPrev = isStep(note.pitch, prev.pitch);
598
+ const leapToNext = Math.abs(next.pitch - note.pitch) >= 3;
599
+ const opposite = note.pitch - prev.pitch > 0 !== next.pitch - note.pitch > 0;
600
+ if (prevMember && nextMember && stepFromPrev && leapToNext && opposite) {
601
+ labels.push({ kind: "escape" });
602
+ handled = true;
603
+ }
604
+ }
605
+ if (!handled && chord && !member) {
606
+ const ic = _chunkFNDWXRRIcjs.intervalAboveRoot.call(void 0, note.pitch, chord);
607
+ const isTension = ic === 2 || ic === 5 || ic === 9;
608
+ const avoid = ic === 5 && chord.intervals.includes(4) && !chord.intervals.includes(5) || ic === 11 && chord.intervals.includes(4) && chord.intervals.includes(10);
609
+ if (avoid) {
610
+ labels.push({ kind: "avoid" });
611
+ const resolveTo = stepResolution(note.pitch, chord);
612
+ if (resolveTo !== void 0) {
613
+ labels.push({ kind: "needsResolution", resolveTo });
614
+ }
615
+ } else if (isTension) {
616
+ labels.push({ kind: "tension", degree: tensionDegree(ic) });
617
+ } else {
618
+ const resolveTo = stepResolution(note.pitch, chord);
619
+ if (resolveTo !== void 0) {
620
+ labels.push({ kind: "needsResolution", resolveTo });
621
+ }
622
+ }
623
+ }
624
+ if (next && _chunkFNDWXRRIcjs.isLeadingToneResolution.call(void 0, note.pitch, next.pitch, key)) {
625
+ labels.push({ kind: "leadingTone", resolveTo: next.pitch });
626
+ }
627
+ const analyzed = {
628
+ noteId: _nullishCoalesce(note.id, () => ( i)),
629
+ pitch: note.pitch,
630
+ startBeat: note.startBeat,
631
+ durationBeat: note.durationBeat,
632
+ labels,
633
+ rationale: describe(labels)
634
+ };
635
+ if (note.originalIndex !== void 0) {
636
+ analyzed.originalIndex = note.originalIndex;
637
+ }
638
+ result.push(analyzed);
639
+ }
640
+ return result;
641
+ }
642
+ function describe(labels) {
643
+ const primary = labels[0];
644
+ if (!primary) {
645
+ return "Unclassified note";
646
+ }
647
+ switch (primary.kind) {
648
+ case "chordTone":
649
+ return `Chord ${primary.role}`;
650
+ case "tension":
651
+ return `${primary.degree}th tension`;
652
+ case "avoid":
653
+ return "Avoid note";
654
+ case "passing":
655
+ return "Passing tone between chord tones";
656
+ case "neighbor":
657
+ return "Neighbor tone";
658
+ case "suspension":
659
+ return `Suspension (${primary.type})`;
660
+ case "anticipation":
661
+ return "Anticipation of the next chord";
662
+ case "escape":
663
+ return "Escape tone";
664
+ case "needsResolution":
665
+ return "Unresolved dissonance";
666
+ case "leadingTone":
667
+ return "Leading tone resolving to the tonic";
668
+ }
669
+ }
670
+
671
+ // src/analyze/arrange/tracks.ts
672
+ function otherVoicesSounding(prepared, excludeTrack, excludeVoice, beat) {
673
+ const out = [];
674
+ for (let t = 0; t < prepared.length; t += 1) {
675
+ const track = prepared[t];
676
+ if (!track || isPercussion(track.role)) {
677
+ continue;
678
+ }
679
+ for (let v = 0; v < track.voices.length; v += 1) {
680
+ if (t === excludeTrack && v === excludeVoice) {
681
+ continue;
682
+ }
683
+ const note = _optionalChain([track, 'access', _25 => _25.voices, 'access', _26 => _26[v], 'optionalAccess', _27 => _27.at, 'call', _28 => _28(beat)]);
684
+ if (note === void 0) {
685
+ continue;
686
+ }
687
+ const snap = { pitch: note.pitch };
688
+ const previous = Math.abs(note.startBeat - beat) <= EPS2 ? note.prevPitch : note.pitch;
689
+ if (previous !== void 0) {
690
+ snap.prevPitch = previous;
691
+ }
692
+ out.push(snap);
693
+ }
694
+ }
695
+ return out;
696
+ }
697
+ function firstSegmentAfter(timeline, beat) {
698
+ let low = 0;
699
+ let high = timeline.segments.length;
700
+ while (low < high) {
701
+ const middle = Math.floor((low + high) / 2);
702
+ if ((_nullishCoalesce(_optionalChain([timeline, 'access', _29 => _29.segments, 'access', _30 => _30[middle], 'optionalAccess', _31 => _31.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat + EPS2) {
703
+ low = middle + 1;
704
+ } else {
705
+ high = middle;
706
+ }
707
+ }
708
+ return low;
709
+ }
710
+ function evaluationBeats(note, timeline) {
711
+ const noteEnd = note.startBeat + note.durationBeat;
712
+ const beats = [note.startBeat];
713
+ for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {
714
+ const start = _optionalChain([timeline, 'access', _32 => _32.segments, 'access', _33 => _33[i], 'optionalAccess', _34 => _34.startBeat]);
715
+ if (start === void 0 || start >= noteEnd - EPS2) {
716
+ break;
717
+ }
718
+ beats.push(start);
719
+ }
720
+ return beats;
721
+ }
722
+ function analyzeArrangement(tracks, opts = {}) {
723
+ const ts = _nullishCoalesce(opts.ts, () => ( _chunk36N73KZ3cjs.parseTimeSignature.call(void 0, "4/4")));
724
+ _chunk7MGZ7D62cjs.assertTimeSignature.call(void 0, ts);
725
+ const budget = opts.budget;
726
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, tracks.length, "arrangement tracks", budget);
727
+ let noteCount = 0;
728
+ for (let index = 0; index < tracks.length; index += 1) {
729
+ const notes = _optionalChain([tracks, 'access', _35 => _35[index], 'optionalAccess', _36 => _36.notes]);
730
+ if (!Array.isArray(notes)) {
731
+ throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
732
+ `tracks[${index}].notes must be an array; received ${typeof notes}`
733
+ );
734
+ }
735
+ _chunk7MGZ7D62cjs.assertNoteEvents.call(void 0, notes, `tracks[${index}].notes`, {
736
+ allowNonPositiveDuration: true,
737
+ budget
738
+ });
739
+ noteCount += notes.length;
740
+ }
741
+ _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, noteCount, "arrangement notes", budget);
742
+ const profile = _nullishCoalesce(opts.profile, () => ( "pop"));
743
+ const minSeverity = _nullishCoalesce(opts.minSeverity, () => ( 1)) /* Warning */;
744
+ const harmonyTracks = opts.harmonyTracks === void 0 ? void 0 : new Set(opts.harmonyTracks.map(Math.trunc));
745
+ const pooled = poolNotes(tracks, harmonyTracks);
746
+ const inferred = opts.timeline === void 0 ? chordTimelineFromNotes(pooled, {
747
+ key: opts.key,
748
+ ts,
749
+ harmonicRhythm: opts.harmonicRhythm
750
+ }) : {
751
+ timeline: opts.timeline,
752
+ key: _nullishCoalesce(_nullishCoalesce(opts.key, () => ( _optionalChain([_chunkCAN76NCUcjs.detectKeyFromNotes.call(void 0, pooled), 'access', _37 => _37[0], 'optionalAccess', _38 => _38.key]))), () => ( _chunkFNDWXRRIcjs.majorKey.call(void 0, 0))),
753
+ segmentConfidence: opts.timeline.segments.map(() => 1)
754
+ };
755
+ const { timeline, key, segmentConfidence } = inferred;
756
+ const cadences = detectCadences(timeline, key);
757
+ const prepared = prepareTracks(tracks);
758
+ const trackAnalyses = [];
759
+ const conflicts = [];
760
+ for (let t = 0; t < prepared.length; t += 1) {
761
+ const track = prepared[t];
762
+ if (!track) {
763
+ continue;
764
+ }
765
+ if (isPercussion(track.role)) {
766
+ trackAnalyses.push({ name: track.name, role: track.role, notes: [] });
767
+ continue;
768
+ }
769
+ const notes = [];
770
+ for (let v = 0; v < track.voices.length; v += 1) {
771
+ const subVoice = track.voices[v];
772
+ if (!subVoice) {
773
+ continue;
774
+ }
775
+ const analyzed = analyzeVoice(
776
+ subVoice.voice,
777
+ timeline.at,
778
+ key,
779
+ (beat) => otherVoicesSounding(prepared, t, v, beat)
780
+ );
781
+ notes.push(...analyzed.map((note) => ({ ...note, trackIndex: track.trackIndex })));
782
+ const labelsById = new Map(analyzed.map((note) => [note.noteId, note.labels]));
783
+ for (let noteIndex = 0; noteIndex < subVoice.voice.length; noteIndex += 1) {
784
+ const note = subVoice.voice[noteIndex];
785
+ const preparedNote = subVoice.sounding[noteIndex];
786
+ if (note === void 0 || preparedNote === void 0) {
787
+ continue;
788
+ }
789
+ for (const beat of evaluationBeats(note, timeline)) {
790
+ const atOnset = Math.abs(beat - note.startBeat) <= EPS2;
791
+ const result = _chunkQ6OP4U43cjs.evaluateSafety.call(void 0, {
792
+ profile,
793
+ candidatePitch: note.pitch,
794
+ prevPitch: atOnset ? preparedNote.prevPitch : note.pitch,
795
+ chord: timeline.at(beat),
796
+ key,
797
+ otherVoices: otherVoicesSounding(prepared, t, v, beat),
798
+ strongBeat: _chunk36N73KZ3cjs.isStrongBeat.call(void 0, beat, ts)
799
+ });
800
+ if (result.safety !== 0 /* Safe */ && result.safety >= minSeverity) {
801
+ const conflict = {
802
+ beat,
803
+ trackName: track.name,
804
+ trackIndex: track.trackIndex,
805
+ noteId: note.id,
806
+ pitch: note.pitch,
807
+ labels: _nullishCoalesce(labelsById.get(note.id), () => ( [])),
808
+ safety: result.safety,
809
+ reasons: result.reasons
810
+ };
811
+ if (note.originalIndex !== void 0) {
812
+ conflict.originalIndex = note.originalIndex;
813
+ }
814
+ if (result.rationale !== void 0) {
815
+ conflict.rationale = result.rationale;
816
+ }
817
+ if (result.resolveTo !== void 0) {
818
+ conflict.resolveTo = result.resolveTo;
819
+ }
820
+ if (result.suggestions !== void 0) {
821
+ conflict.suggestions = [...result.suggestions];
822
+ }
823
+ conflicts.push(conflict);
824
+ }
825
+ }
826
+ }
827
+ }
828
+ notes.sort((a, b) => a.noteId - b.noteId);
829
+ trackAnalyses.push({ name: track.name, role: track.role, notes });
830
+ }
831
+ conflicts.sort((a, b) => b.safety - a.safety || a.beat - b.beat);
832
+ return { key, timeline, segmentConfidence, cadences, tracks: trackAnalyses, conflicts };
833
+ }
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+ exports.chordTimelineFromChords = chordTimelineFromChords; exports.chordTimelineFromNotes = chordTimelineFromNotes; exports.detectCadences = detectCadences; exports.tensionCurve = tensionCurve; exports.tensionCurveFrom = tensionCurveFrom; exports.toVoiceNotes = toVoiceNotes; exports.analyzeVoice = analyzeVoice; exports.analyzeArrangement = analyzeArrangement;
845
+ //# sourceMappingURL=chunk-RC6GBMCD.cjs.map