@williamzujkowski/live-coding-music-mcp 4.0.0 → 4.1.1
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.
- package/README.md +202 -705
- package/dist/AudioAnalyzer.js +1294 -91
- package/dist/PatternStore.js +276 -23
- package/dist/StrudelController.js +388 -56
- package/dist/server/resources.js +27 -20
- package/dist/server/server.js +384 -76
- package/dist/server/tools/ai.js +124 -91
- package/dist/server/tools/analysis.js +168 -59
- package/dist/server/tools/capture.js +135 -24
- package/dist/server/tools/compose.js +26 -26
- package/dist/server/tools/diagnostics.js +20 -3
- package/dist/server/tools/editor.js +60 -6
- package/dist/server/tools/generate.js +38 -15
- package/dist/server/tools/history.js +30 -8
- package/dist/server/tools/playback.js +2 -1
- package/dist/server/tools/session.js +38 -6
- package/dist/server/tools/storage.js +179 -8
- package/dist/server/tools/transform.js +127 -43
- package/dist/server/tools/types.js +209 -14
- package/dist/services/AudioCaptureService.js +302 -33
- package/dist/services/AudioExportService.js +318 -0
- package/dist/services/BrowserOnlyFunctions.js +149 -0
- package/dist/services/EventDensityProbe.js +324 -0
- package/dist/services/GeminiService.js +221 -36
- package/dist/services/IsolatedEngineRunner.js +308 -0
- package/dist/services/IsolatedStrudelEngine.js +135 -0
- package/dist/services/LocalPatternEngine.js +16 -0
- package/dist/services/MIDIExportService.js +765 -71
- package/dist/services/MIDIImportService.js +474 -0
- package/dist/services/MusicTheory.js +85 -13
- package/dist/services/PatternGenerator.js +168 -85
- package/dist/services/PatternSandbox.js +359 -0
- package/dist/services/SessionManager.js +338 -65
- package/dist/services/StrudelEngine.js +218 -12
- package/dist/services/StrudelEngineHelpers.js +24 -8
- package/dist/services/StyleRegistry.js +204 -0
- package/dist/services/Transferable.js +114 -0
- package/dist/services/ai/AiTransport.js +56 -0
- package/dist/services/ai/AudioMeasurements.js +155 -0
- package/dist/services/ai/CliTransport.js +256 -0
- package/dist/services/engineChild.js +83 -0
- package/dist/services/engineChildPath.js +28 -0
- package/dist/utils/CategorisedError.js +59 -0
- package/dist/utils/ErrorRecovery.js +130 -110
- package/dist/utils/InputValidator.js +55 -7
- package/dist/utils/PatternValidator.js +136 -17
- package/dist/utils/SafePath.js +112 -0
- package/dist/utils/ServerConfig.js +248 -0
- package/dist/utils/TableLookup.js +41 -0
- package/dist/utils/Tempo.js +143 -0
- package/package.json +27 -16
- package/dist/services/__mocks__/StrudelEngine.js +0 -157
- package/dist/types/index.js +0 -1
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides whether a pattern query is safe to materialize (#360).
|
|
3
|
+
*
|
|
4
|
+
* ## What was wrong with the old guard
|
|
5
|
+
*
|
|
6
|
+
* `queryEvents` sampled the first 1e-4 of the requested range, extrapolated,
|
|
7
|
+
* and refused if the projection exceeded the cap. Measured, that guard has
|
|
8
|
+
* two independent holes:
|
|
9
|
+
*
|
|
10
|
+
* s("bd*200000") -> refused correctly
|
|
11
|
+
* s("~ bd*200000") -> OUT OF HEAP. One leading rest, and the probe
|
|
12
|
+
* sees nothing, projects nothing, refuses nothing.
|
|
13
|
+
* s("[bd*99999]*99999") -> OUT OF HEAP. 1e-4 of a 10^10-event cycle is
|
|
14
|
+
* still 10^6 events; the probe dies before it
|
|
15
|
+
* can refuse.
|
|
16
|
+
*
|
|
17
|
+
* The second is the cheaper attack: it needs no density in the probe
|
|
18
|
+
* window at all, just ordinary musical notation.
|
|
19
|
+
*
|
|
20
|
+
* ## What this does instead
|
|
21
|
+
*
|
|
22
|
+
* Probes at several offsets spread across the range — so a rest at the
|
|
23
|
+
* start cannot hide anything — with the span grown geometrically from
|
|
24
|
+
* very small, so a dense pattern is caught while the sample is still
|
|
25
|
+
* tiny.
|
|
26
|
+
*
|
|
27
|
+
* ## The rule that makes it safe to extrapolate
|
|
28
|
+
*
|
|
29
|
+
* Measured, and it is the part that is easy to get wrong:
|
|
30
|
+
*
|
|
31
|
+
* s("bd").slow(64) at span 1e-12 -> 1 hap
|
|
32
|
+
* stack(s("bd*4"), s("hh*8")) at span 1e-12 -> 2 haps
|
|
33
|
+
*
|
|
34
|
+
* Naively projecting the first gives 10^12 events and refuses one of the
|
|
35
|
+
* most ordinary patterns there is. Events legitimately begin at t=0, and
|
|
36
|
+
* a window narrower than the true inter-onset interval ALWAYS
|
|
37
|
+
* over-projects. So a projection is only taken once the sample looks
|
|
38
|
+
* like a sample: at least `minDistinctOnsets` DISTINCT onset times
|
|
39
|
+
* inside the window. Distinct times, not hap count — a stack of N layers
|
|
40
|
+
* all starting together is one moment observed N times, and says nothing
|
|
41
|
+
* about density.
|
|
42
|
+
*
|
|
43
|
+
* (The old `PROBE_FRACTION = 1e-4` avoided this by arithmetic accident:
|
|
44
|
+
* one stray hap projects to 10^4, which happens to sit under the 50,000
|
|
45
|
+
* cap. Any smaller constant would have begun refusing real patterns.)
|
|
46
|
+
*
|
|
47
|
+
* ## Residual
|
|
48
|
+
*
|
|
49
|
+
* Sampling cannot be sound against arbitrary non-uniform density. Three
|
|
50
|
+
* shapes get past this, all of them contained since #307 as an error
|
|
51
|
+
* envelope and a respawned child rather than a dead server:
|
|
52
|
+
*
|
|
53
|
+
* - density confined to a gap between every window, at every rung;
|
|
54
|
+
* - a burst so dense that growing a window into it from an adjacent
|
|
55
|
+
* rest is itself fatal, since growth is what discovers it;
|
|
56
|
+
* - clustered events sitting exactly on a slice boundary, which the
|
|
57
|
+
* phase offset deliberately steps away from.
|
|
58
|
+
*
|
|
59
|
+
* The last is the price of decorrelating from the cycle, and it is worth
|
|
60
|
+
* paying: without the offset, EVERY window on a multi-cycle range shares
|
|
61
|
+
* one phase, and a leading rest hides a pattern from all of them.
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* Windows spread across the range. This is the guard's resolution: a
|
|
65
|
+
* dense region narrower than 1/OFFSETS of the requested range, sitting
|
|
66
|
+
* between two windows, is not seen.
|
|
67
|
+
*
|
|
68
|
+
* Six was tried and rejected — it misses density confined to the last
|
|
69
|
+
* EIGHTH, which is an ordinary thing for a pattern to do. Coverage is
|
|
70
|
+
* worth more here than the ~15ms it costs, because this runs on
|
|
71
|
+
* query_pattern_events, a diagnostic, and not on the write path.
|
|
72
|
+
*/
|
|
73
|
+
export const DEFAULT_OFFSETS = 8;
|
|
74
|
+
export const DEFAULT_MIN_DISTINCT_ONSETS = 16;
|
|
75
|
+
/**
|
|
76
|
+
* Onsets a window should hold before its density is believed.
|
|
77
|
+
*
|
|
78
|
+
* Small samples are unreliable in both directions and the error is not
|
|
79
|
+
* academic: measured, a 1e-3 window at t=0 of `s("bd*40000")` holds 80
|
|
80
|
+
* onsets where the cycle average is 40, so extrapolating from it
|
|
81
|
+
* projects 80,000 against a 50,000 cap and refuses a pattern that is
|
|
82
|
+
* comfortably legal. Growing the window until it holds a real sample
|
|
83
|
+
* costs a few thousand haps and removes the guesswork; it is a much
|
|
84
|
+
* better answer than a fudge factor on the threshold.
|
|
85
|
+
*/
|
|
86
|
+
export const DEFAULT_SAMPLE_TARGET = 2000;
|
|
87
|
+
/**
|
|
88
|
+
* How far above the cap a SMALL sample must project before it is
|
|
89
|
+
* believed without growing the window.
|
|
90
|
+
*
|
|
91
|
+
* This is the escape hatch for the pathological case: at 10^10 events
|
|
92
|
+
* per cycle there is no growing the window — the next step up would
|
|
93
|
+
* materialize more than the heap holds. A sample projecting ten times
|
|
94
|
+
* the cap is not a borderline call, and waiting for a bigger one is how
|
|
95
|
+
* the old guard died.
|
|
96
|
+
*/
|
|
97
|
+
export const OBVIOUSLY_HUGE_MULTIPLE = 10;
|
|
98
|
+
/**
|
|
99
|
+
* Three rungs, not five. Every rung costs a query at every offset, and a
|
|
100
|
+
* `queryArc` call has ~1.8 ms of fixed overhead regardless of how little
|
|
101
|
+
* it returns — so the ladder's length, not its contents, is what the
|
|
102
|
+
* ordinary sparse pattern pays for.
|
|
103
|
+
*
|
|
104
|
+
* The first rung has to be tiny or the probe is the thing that runs out
|
|
105
|
+
* of heap: 1e-4 of a 10^10-event cycle is a million events, which is how
|
|
106
|
+
* the guard this replaced died. From there, three orders of magnitude a
|
|
107
|
+
* step reaches a usable sample for anything the cap cares about.
|
|
108
|
+
*/
|
|
109
|
+
export const DEFAULT_LADDER = [1e-8, 1e-6, 1e-4, 1e-2];
|
|
110
|
+
/**
|
|
111
|
+
* Fractional part of the golden ratio, used to offset each window within
|
|
112
|
+
* its own slice.
|
|
113
|
+
*
|
|
114
|
+
* Evenly spaced windows are not enough, and the failure is not subtle.
|
|
115
|
+
* `query_pattern_events` accepts up to 16 cycles; with 8 windows across
|
|
116
|
+
* 16 cycles every window starts on a whole cycle — the SAME PHASE, eight
|
|
117
|
+
* times. So a pattern with a leading rest hides from all of them:
|
|
118
|
+
*
|
|
119
|
+
* s("~ bd*200000") over 0..1 -> refused
|
|
120
|
+
* s("~ bd*200000") over 0..4 -> OUT OF HEAP
|
|
121
|
+
* s("~ bd*200000") over 0..16 -> OUT OF HEAP
|
|
122
|
+
*
|
|
123
|
+
* Musical patterns repeat per cycle, so any window placement that is
|
|
124
|
+
* commensurate with the cycle samples one phase repeatedly and calls it
|
|
125
|
+
* coverage. An irrational step decorrelates the phases while keeping one
|
|
126
|
+
* window per slice, which pure golden-ratio placement would not — that
|
|
127
|
+
* leaves whole slices empty.
|
|
128
|
+
*/
|
|
129
|
+
const GOLDEN_FRACTION = 0.618033988749895;
|
|
130
|
+
/**
|
|
131
|
+
* Hard ceiling on a probe window, in cycles, whatever the range.
|
|
132
|
+
*
|
|
133
|
+
* Ladder rungs are fractions of the requested range, and the range may be
|
|
134
|
+
* 16 cycles — so the top rung would be 0.16 cycles wide, and a window
|
|
135
|
+
* that starts in a rest and grows across the boundary into a dense region
|
|
136
|
+
* materializes in proportion to how far it reaches. Capping the absolute
|
|
137
|
+
* width bounds that exposure by 16x on the widest ranges without
|
|
138
|
+
* changing any verdict: density is per cycle, so a smaller window
|
|
139
|
+
* measures it just as well.
|
|
140
|
+
*/
|
|
141
|
+
const MAX_PROBE_CYCLES = 0.01;
|
|
142
|
+
/**
|
|
143
|
+
* Decides whether querying `[start, end)` is safe.
|
|
144
|
+
*
|
|
145
|
+
* @param queryArc - The pattern's own query function
|
|
146
|
+
* @param start - Range start, in cycles
|
|
147
|
+
* @param end - Range end, in cycles
|
|
148
|
+
* @param options - Cap and probe shape
|
|
149
|
+
* @returns A refusal with its evidence, or permission to proceed
|
|
150
|
+
*/
|
|
151
|
+
export function probeEventDensity(queryArc, start, end, options) {
|
|
152
|
+
const span = end - start;
|
|
153
|
+
const offsets = options.offsets ?? DEFAULT_OFFSETS;
|
|
154
|
+
const minDistinct = options.minDistinctOnsets ?? DEFAULT_MIN_DISTINCT_ONSETS;
|
|
155
|
+
const sampleTarget = options.sampleTarget ?? DEFAULT_SAMPLE_TARGET;
|
|
156
|
+
const ladder = options.ladder ?? DEFAULT_LADDER;
|
|
157
|
+
const cap = options.maxEvents;
|
|
158
|
+
let queries = 0;
|
|
159
|
+
let observedOnsets = 0;
|
|
160
|
+
/** Onsets and window width accumulated across every BELIEVABLE sample. */
|
|
161
|
+
let sampledOnsets = 0;
|
|
162
|
+
let sampledSpan = 0;
|
|
163
|
+
/**
|
|
164
|
+
* Every onset seen in a decision sample, believable or not.
|
|
165
|
+
*
|
|
166
|
+
* Windows are disjoint, so this is a LOWER BOUND on the events inside
|
|
167
|
+
* the requested range — evidence rather than arithmetic. It is the only
|
|
168
|
+
* thing that catches clustered events: eight chords of ten thousand
|
|
169
|
+
* layers hold 80,000 events and describe no density at all, so the
|
|
170
|
+
* distinctness gate rejects every sample and the projection has nothing
|
|
171
|
+
* to work with. Counting still works. (Found by cross-model review
|
|
172
|
+
* after my first attempt refused to accumulate unbelievable samples and
|
|
173
|
+
* let exactly this through.)
|
|
174
|
+
*/
|
|
175
|
+
let countedFloor = 0;
|
|
176
|
+
/** How many windows contributed anything. Used to sanity-check an empty result. */
|
|
177
|
+
let windowsWithOnsets = 0;
|
|
178
|
+
if (span <= 0 || offsets < 1) {
|
|
179
|
+
return { kind: 'proceed', queries, observedOnsets, windowsWithOnsets: 0 };
|
|
180
|
+
}
|
|
181
|
+
const step = span / offsets;
|
|
182
|
+
for (let i = 0; i < offsets; i++) {
|
|
183
|
+
// Where in this slice the window sits. Irrational in the cycle, so
|
|
184
|
+
// eight windows sample eight different phases rather than the same
|
|
185
|
+
// one eight times — see GOLDEN_FRACTION.
|
|
186
|
+
const phase = (i * GOLDEN_FRACTION) % 1;
|
|
187
|
+
for (let rung = 0; rung < ladder.length; rung++) {
|
|
188
|
+
const fraction = ladder[rung];
|
|
189
|
+
const probeSpan = Math.min(span * fraction, MAX_PROBE_CYCLES);
|
|
190
|
+
if (probeSpan <= 0)
|
|
191
|
+
continue;
|
|
192
|
+
// Slid within its own slice, and never so far that the window runs
|
|
193
|
+
// past the slice's end — which would put the last one past `end`.
|
|
194
|
+
const windowStart = start + i * step + phase * (step - probeSpan);
|
|
195
|
+
// Whether there is anywhere left to grow. The largest sample this
|
|
196
|
+
// offset can produce still has to be USED — discarding it because
|
|
197
|
+
// it fell short of the target is how a pattern at 120,000 events
|
|
198
|
+
// per cycle walked straight past the guard.
|
|
199
|
+
const canGrow = rung + 1 < ladder.length;
|
|
200
|
+
const windowEnd = windowStart + probeSpan;
|
|
201
|
+
const haps = queryArc(windowStart, windowEnd);
|
|
202
|
+
queries++;
|
|
203
|
+
// Onsets only. A note held across the window began somewhere else
|
|
204
|
+
// and is not evidence of density here.
|
|
205
|
+
const onsets = [];
|
|
206
|
+
for (const hap of haps) {
|
|
207
|
+
const begin = hap.whole?.begin.valueOf();
|
|
208
|
+
if (begin === undefined)
|
|
209
|
+
continue;
|
|
210
|
+
if (begin >= windowStart && begin < windowEnd)
|
|
211
|
+
onsets.push(begin);
|
|
212
|
+
}
|
|
213
|
+
observedOnsets = Math.max(observedOnsets, onsets.length);
|
|
214
|
+
// Counted, not projected. Every one of these onsets is inside the
|
|
215
|
+
// requested range, so seeing cap-many in a single window proves the
|
|
216
|
+
// range holds at least that many — no extrapolation, and no
|
|
217
|
+
// distinctness gate to pass.
|
|
218
|
+
//
|
|
219
|
+
// This is the hole cross-model review (agy) found: a pattern with
|
|
220
|
+
// thousands of layers starting together clears the sample target
|
|
221
|
+
// while failing the distinctness gate, and fell out of the ladder
|
|
222
|
+
// without a verdict. Simultaneous events say nothing about DENSITY,
|
|
223
|
+
// but they are still events, and there can be too many of them.
|
|
224
|
+
if (onsets.length >= cap) {
|
|
225
|
+
return {
|
|
226
|
+
kind: 'refuse',
|
|
227
|
+
projected: onsets.length,
|
|
228
|
+
sampleCount: onsets.length,
|
|
229
|
+
spanFraction: probeSpan / span,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// From the actual window width, not the nominal rung: the two
|
|
233
|
+
// differ once MAX_PROBE_CYCLES bites, and using the rung would
|
|
234
|
+
// under-project by the same factor.
|
|
235
|
+
const projected = Math.round((onsets.length * span) / probeSpan);
|
|
236
|
+
const distinct = new Set(onsets).size;
|
|
237
|
+
// Enough separate moments for the count to describe a rate rather
|
|
238
|
+
// than a chord. See minDistinctOnsets.
|
|
239
|
+
const believable = distinct >= minDistinct;
|
|
240
|
+
// Obviously huge: refuse on a small sample rather than grow the
|
|
241
|
+
// window into the heap. Only this shortcut acts on one window;
|
|
242
|
+
// everything nearer the cap is decided in aggregate below.
|
|
243
|
+
if (believable && projected > cap * OBVIOUSLY_HUGE_MULTIPLE) {
|
|
244
|
+
return {
|
|
245
|
+
kind: 'refuse',
|
|
246
|
+
projected,
|
|
247
|
+
sampleCount: onsets.length,
|
|
248
|
+
// The width actually queried, which is not the rung once
|
|
249
|
+
// MAX_PROBE_CYCLES clamps it.
|
|
250
|
+
spanFraction: probeSpan / span,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
// A sample big enough to trust — or the biggest this offset will
|
|
254
|
+
// ever produce. Either way, record it and move to the next offset.
|
|
255
|
+
if (onsets.length >= sampleTarget || !canGrow) {
|
|
256
|
+
countedFloor += onsets.length;
|
|
257
|
+
if (onsets.length > 0)
|
|
258
|
+
windowsWithOnsets++;
|
|
259
|
+
if (countedFloor > cap) {
|
|
260
|
+
return {
|
|
261
|
+
kind: 'refuse',
|
|
262
|
+
projected: countedFloor,
|
|
263
|
+
sampleCount: countedFloor,
|
|
264
|
+
spanFraction: probeSpan / span,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// EVERY window that reaches here counts, distinct or not.
|
|
268
|
+
//
|
|
269
|
+
// The distinctness gate exists to stop a rate being extrapolated
|
|
270
|
+
// from a handful of stacked events — `s("bd").slow(64)` returns
|
|
271
|
+
// one hap at any span, and projecting from it refused ordinary
|
|
272
|
+
// patterns (#360 rule 1). It was never meant to discard a window
|
|
273
|
+
// that is *full*: a window holding `sampleTarget` events is
|
|
274
|
+
// evidence of density whatever their onset spelling.
|
|
275
|
+
//
|
|
276
|
+
// Discarding it let a pattern through that the probe exists to
|
|
277
|
+
// refuse. Measured, 150,000 onset instants x 250 simultaneous
|
|
278
|
+
// layers — 37,500,000 events in one cycle:
|
|
279
|
+
//
|
|
280
|
+
// before: {"kind":"proceed","observedOnsets":3750,
|
|
281
|
+
// "windowsWithOnsets":8}
|
|
282
|
+
//
|
|
283
|
+
// Every one of the eight windows saw the density and none of
|
|
284
|
+
// them was allowed to say so: each cleared `sampleTarget` (so
|
|
285
|
+
// the ladder stopped) but failed `minDistinct` (so it was not
|
|
286
|
+
// recorded), and `countedFloor` stayed under the cap because
|
|
287
|
+
// eight small windows hold few events in absolute terms (#460).
|
|
288
|
+
//
|
|
289
|
+
// Unconditional, and it has to say so. The fix for that wrote
|
|
290
|
+
// `if (believable || windowIsFull || !canGrow)` — inside a
|
|
291
|
+
// branch already guarded by `onsets.length >= sampleTarget ||
|
|
292
|
+
// !canGrow`, which implies it. The condition was always true,
|
|
293
|
+
// so `believable` had no effect here and the code described a
|
|
294
|
+
// gate it did not have. Reaching this line means the window is
|
|
295
|
+
// full or maximal; both count, so there is nothing to test
|
|
296
|
+
// (#464). `believable` still gates the obviously-huge shortcut
|
|
297
|
+
// above, which is the place the distinctness rule belongs.
|
|
298
|
+
sampledOnsets += onsets.length;
|
|
299
|
+
sampledSpan += probeSpan;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Averaged across every window, not taken from the densest one.
|
|
305
|
+
//
|
|
306
|
+
// The max is right for the obvious cases and wrong for the borderline:
|
|
307
|
+
// measured, a window at t=0 of `s("bd*40000")` sees twice the cycle's
|
|
308
|
+
// average density, so projecting from it gives 80,000 against a 50,000
|
|
309
|
+
// cap and refuses a pattern holding 40,001 events. Averaging over
|
|
310
|
+
// windows spread across the range is what makes the near-cap verdict
|
|
311
|
+
// trustworthy; the shortcut above still handles anything wildly over.
|
|
312
|
+
if (sampledSpan > 0) {
|
|
313
|
+
const projected = Math.round((sampledOnsets * span) / sampledSpan);
|
|
314
|
+
if (projected > cap) {
|
|
315
|
+
return {
|
|
316
|
+
kind: 'refuse',
|
|
317
|
+
projected,
|
|
318
|
+
sampleCount: sampledOnsets,
|
|
319
|
+
spanFraction: sampledSpan / span,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { kind: 'proceed', queries, observedOnsets, windowsWithOnsets };
|
|
324
|
+
}
|