@map-audio/pam-mcp-server 1.18.1 → 1.22.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.
- package/LICENSE +21 -0
- package/README.md +5 -3
- package/dist/audioAnalysis.d.ts +35 -0
- package/dist/audioAnalysis.js +277 -0
- package/dist/audioAnalysis.js.map +1 -0
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.js +113 -24
- package/dist/bridge.js.map +1 -1
- package/dist/corpusFingerprints.d.ts +17 -0
- package/dist/corpusFingerprints.js +10 -0
- package/dist/corpusFingerprints.js.map +1 -0
- package/dist/corpusRanges.d.ts +28 -0
- package/dist/corpusRanges.js +1029 -0
- package/dist/corpusRanges.js.map +1 -0
- package/dist/drumSynth.d.ts +96 -0
- package/dist/drumSynth.js +193 -0
- package/dist/drumSynth.js.map +1 -0
- package/dist/drumSynthEngines.json +1736 -0
- package/dist/fingerprint.d.ts +70 -0
- package/dist/fingerprint.js +240 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/generated/variationSnapshot.d.ts +31 -0
- package/dist/generated/variationSnapshot.js +62 -0
- package/dist/generated/variationSnapshot.js.map +1 -0
- package/dist/manifest-cell1.json +5414 -396
- package/dist/manifest-mfx.json +12013 -1714
- package/dist/manifest.d.ts +24 -0
- package/dist/manifest.js +60 -5
- package/dist/manifest.js.map +1 -1
- package/dist/manifest.json +24881 -547
- package/dist/mfxCombos.d.ts +45 -0
- package/dist/mfxCombos.js +499 -0
- package/dist/mfxCombos.js.map +1 -0
- package/dist/mixAssist.d.ts +6 -0
- package/dist/mixAssist.js +101 -11
- package/dist/mixAssist.js.map +1 -1
- package/dist/productProfile.d.ts +9 -0
- package/dist/productProfile.js +20 -0
- package/dist/productProfile.js.map +1 -0
- package/dist/server.js +1979 -342
- package/dist/server.js.map +1 -1
- package/dist/variationSessionKeys.json +110 -0
- package/package.json +10 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export type Fingerprint = {
|
|
2
|
+
name: string;
|
|
3
|
+
bpm: number;
|
|
4
|
+
/** Cells carrying at least one note. */
|
|
5
|
+
cells: number;
|
|
6
|
+
/** Per-cell param suffixes moved off their manifest default, unioned. */
|
|
7
|
+
params: string[];
|
|
8
|
+
/** Effect slugs enabled on at least one cell. */
|
|
9
|
+
fx: string[];
|
|
10
|
+
/** Distinct FX chain orders, as "filter>eq>..." signatures. */
|
|
11
|
+
chains: string[];
|
|
12
|
+
/** Modulation source counts and the param families they target. */
|
|
13
|
+
mod: {
|
|
14
|
+
lfo: number;
|
|
15
|
+
env: number;
|
|
16
|
+
vary: number;
|
|
17
|
+
seq: number;
|
|
18
|
+
targets: string[];
|
|
19
|
+
};
|
|
20
|
+
/** Per-cell onset sets, quantized to 16ths of a bar: "c1:0,4,8,12". */
|
|
21
|
+
groove: string[];
|
|
22
|
+
/** Distinct transposition values in use (pitchShift + arpeg + pitch lanes). */
|
|
23
|
+
pitch: number[];
|
|
24
|
+
/** Sample identity, normalized so takes of one source collapse together. */
|
|
25
|
+
sources: string[];
|
|
26
|
+
/** Drum-synth engine indices in use. */
|
|
27
|
+
engines: number[];
|
|
28
|
+
};
|
|
29
|
+
export declare function buildFingerprint(state: Record<string, unknown>, defaults: Record<string, number>, effectNames: readonly string[], name?: string): Fingerprint;
|
|
30
|
+
/** Dimension weights. Groove and params dominate because they are what a
|
|
31
|
+
* listener names first when two presets "sound the same". */
|
|
32
|
+
declare const WEIGHTS: {
|
|
33
|
+
params: number;
|
|
34
|
+
groove: number;
|
|
35
|
+
mod: number;
|
|
36
|
+
sources: number;
|
|
37
|
+
fx: number;
|
|
38
|
+
chains: number;
|
|
39
|
+
pitch: number;
|
|
40
|
+
};
|
|
41
|
+
export type Similarity = {
|
|
42
|
+
overall: number;
|
|
43
|
+
byDimension: Record<keyof typeof WEIGHTS, number>;
|
|
44
|
+
};
|
|
45
|
+
export declare function similarity(a: Fingerprint, b: Fingerprint): Similarity;
|
|
46
|
+
/** Leave-one-out novelty distribution of the corpus against itself. Absolute
|
|
47
|
+
* novelty numbers mean nothing on their own — a perfectly good preset scores
|
|
48
|
+
* ~0.55 because every PAM preset shares an instrument. The only useful
|
|
49
|
+
* question is "distinct compared to what already shipped", so verdicts are
|
|
50
|
+
* phrased against these percentiles rather than invented thresholds. */
|
|
51
|
+
export type NoveltyBaseline = {
|
|
52
|
+
p10: number;
|
|
53
|
+
p50: number;
|
|
54
|
+
p90: number;
|
|
55
|
+
floor: number;
|
|
56
|
+
};
|
|
57
|
+
export type NeighbourReport = {
|
|
58
|
+
novelty: number;
|
|
59
|
+
percentileVsCorpus: number | null;
|
|
60
|
+
nearest: {
|
|
61
|
+
name: string;
|
|
62
|
+
similarity: number;
|
|
63
|
+
duplicatedDimensions: string[];
|
|
64
|
+
}[];
|
|
65
|
+
verdict: string;
|
|
66
|
+
};
|
|
67
|
+
/** Score a candidate against a corpus. novelty = 1 - closest match, so 0 means
|
|
68
|
+
* "this already exists" and 1 means "nothing like it has shipped". */
|
|
69
|
+
export declare function scoreNovelty(candidate: Fingerprint, corpus: Fingerprint[], topN?: number, baseline?: NoveltyBaseline): NeighbourReport;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Preset fingerprinting — the shared algorithm behind the corpus index
|
|
2
|
+
// (scripts/derive-corpus-fingerprints.ts) and the preset_novelty tool.
|
|
3
|
+
//
|
|
4
|
+
// WHY: nothing in the design pipeline ever compared a new preset against the
|
|
5
|
+
// ones already shipped, so every session started from the same prior and landed
|
|
6
|
+
// on the same centroid preset. A fingerprint plus a distance makes "does this
|
|
7
|
+
// already exist?" a number instead of a judgement call.
|
|
8
|
+
//
|
|
9
|
+
// The dimensions are deliberately structural rather than acoustic — they read
|
|
10
|
+
// straight off preset state, so novelty can be scored with no render, no audio
|
|
11
|
+
// and no plugin connection.
|
|
12
|
+
const asArray = (v) => {
|
|
13
|
+
if (Array.isArray(v))
|
|
14
|
+
return v;
|
|
15
|
+
if (typeof v === "string") {
|
|
16
|
+
try {
|
|
17
|
+
const p = JSON.parse(v);
|
|
18
|
+
return Array.isArray(p) ? p : [];
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return [];
|
|
25
|
+
};
|
|
26
|
+
const asRecord = (v) => {
|
|
27
|
+
if (typeof v === "string") {
|
|
28
|
+
try {
|
|
29
|
+
const p = JSON.parse(v);
|
|
30
|
+
return p && typeof p === "object" ? p : {};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return v && typeof v === "object" ? v : {};
|
|
37
|
+
};
|
|
38
|
+
/** Strip take numbers, bpm tags and separators so "Kick_03_120bpm" and
|
|
39
|
+
* "kick-05" read as the same source. Two presets built from the same folder
|
|
40
|
+
* should look similar even when the exact files differ. */
|
|
41
|
+
const normalizeSource = (path) => path
|
|
42
|
+
.split(/[\\/]/)
|
|
43
|
+
.pop()
|
|
44
|
+
.replace(/\.[a-z0-9]+$/i, "")
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.replace(/\d+\s*bpm/g, "")
|
|
47
|
+
.replace(/[\d_\-\s]+/g, "")
|
|
48
|
+
.slice(0, 24);
|
|
49
|
+
/** Param suffix from a modulation link's target, so cell3_filterCutoff and
|
|
50
|
+
* cell7_filterCutoff count as the same kind of move. */
|
|
51
|
+
const targetFamily = (paramId) => typeof paramId === "string" ? paramId.replace(/^cell\d+_/, "").replace(/^macro_/, "macro:") : null;
|
|
52
|
+
export function buildFingerprint(state, defaults, effectNames, name = "candidate") {
|
|
53
|
+
const params = new Set();
|
|
54
|
+
const fx = new Set();
|
|
55
|
+
const chains = new Set();
|
|
56
|
+
const groove = [];
|
|
57
|
+
const pitch = new Set();
|
|
58
|
+
const engines = new Set();
|
|
59
|
+
let cells = 0;
|
|
60
|
+
for (let c = 1; c <= 8; c += 1) {
|
|
61
|
+
const seq = asArray(state[`cell${c}_midiSequence`]);
|
|
62
|
+
if (seq.length === 0)
|
|
63
|
+
continue;
|
|
64
|
+
cells += 1;
|
|
65
|
+
// The per-effect enable flags are state-only — they are NOT manifest
|
|
66
|
+
// parameters, so they never reach the defaults map below and have to be
|
|
67
|
+
// read directly. Missing this silently produced an empty fx set on every
|
|
68
|
+
// preset in the corpus index.
|
|
69
|
+
for (const slug of effectNames) {
|
|
70
|
+
if (state[`cell${c}_${slug}Enabled`])
|
|
71
|
+
fx.add(slug);
|
|
72
|
+
}
|
|
73
|
+
for (const [key, value] of Object.entries(state)) {
|
|
74
|
+
if (!key.startsWith(`cell${c}_`) || typeof value !== "number")
|
|
75
|
+
continue;
|
|
76
|
+
const suffix = key.slice(`cell${c}_`.length);
|
|
77
|
+
const def = defaults[suffix];
|
|
78
|
+
// A param at its default was never a decision. Only moved params
|
|
79
|
+
// describe the preset.
|
|
80
|
+
if (def === undefined || value === def)
|
|
81
|
+
continue;
|
|
82
|
+
params.add(suffix);
|
|
83
|
+
}
|
|
84
|
+
const order = asArray(state[`cell${c}_effectOrder`]).filter((s) => typeof s === "string");
|
|
85
|
+
if (order.length)
|
|
86
|
+
chains.add(order.join(">"));
|
|
87
|
+
// Onsets quantized to 16ths of a bar. `time` is in beats, so ×4 is a 16th
|
|
88
|
+
// grid; folding mod 16 compares the groove rather than the bar count.
|
|
89
|
+
const steps = new Set();
|
|
90
|
+
for (const ev of seq) {
|
|
91
|
+
const t = ev.time;
|
|
92
|
+
if (typeof t === "number")
|
|
93
|
+
steps.add(Math.round(t * 4) % 16);
|
|
94
|
+
}
|
|
95
|
+
if (steps.size)
|
|
96
|
+
groove.push(`c${c}:${[...steps].sort((a, b) => a - b).join(",")}`);
|
|
97
|
+
const ps = state[`cell${c}_pitchShift`];
|
|
98
|
+
if (typeof ps === "number" && ps !== 0)
|
|
99
|
+
pitch.add(ps);
|
|
100
|
+
const ar = state[`cell${c}_arpeg`];
|
|
101
|
+
if (typeof ar === "number" && ar !== 0)
|
|
102
|
+
pitch.add(ar);
|
|
103
|
+
for (const ev of asArray(state[`cell${c}_pitchSequence`])) {
|
|
104
|
+
const v = ev.value;
|
|
105
|
+
if (typeof v === "number" && v !== 0)
|
|
106
|
+
pitch.add(v);
|
|
107
|
+
}
|
|
108
|
+
const eng = state[`cell${c}_synthEngine`];
|
|
109
|
+
if (typeof eng === "number" && eng > 0)
|
|
110
|
+
engines.add(eng);
|
|
111
|
+
}
|
|
112
|
+
const modTargets = new Set();
|
|
113
|
+
const countLinks = (key) => {
|
|
114
|
+
const links = asArray(state[key]);
|
|
115
|
+
for (const l of links) {
|
|
116
|
+
const fam = targetFamily(l.paramId);
|
|
117
|
+
if (fam)
|
|
118
|
+
modTargets.add(fam);
|
|
119
|
+
}
|
|
120
|
+
return links.length;
|
|
121
|
+
};
|
|
122
|
+
const sources = new Set();
|
|
123
|
+
const cellMap = asRecord(state.cells);
|
|
124
|
+
for (const entry of Object.values(cellMap)) {
|
|
125
|
+
const fp = entry.filePath;
|
|
126
|
+
if (typeof fp === "string" && fp.length > 2)
|
|
127
|
+
sources.add(normalizeSource(fp));
|
|
128
|
+
}
|
|
129
|
+
const bpmRaw = asRecord(state.transport).bpm ?? state.bpm;
|
|
130
|
+
return {
|
|
131
|
+
name,
|
|
132
|
+
bpm: typeof bpmRaw === "number" ? bpmRaw : 0,
|
|
133
|
+
cells,
|
|
134
|
+
params: [...params].sort(),
|
|
135
|
+
fx: [...fx].sort(),
|
|
136
|
+
chains: [...chains].sort(),
|
|
137
|
+
mod: {
|
|
138
|
+
lfo: countLinks("lfos"),
|
|
139
|
+
env: countLinks("envelopes"),
|
|
140
|
+
vary: countLinks("vary"),
|
|
141
|
+
seq: countLinks("paramSeqs"),
|
|
142
|
+
targets: [...modTargets].sort(),
|
|
143
|
+
},
|
|
144
|
+
groove: groove.sort(),
|
|
145
|
+
pitch: [...pitch].sort((a, b) => a - b),
|
|
146
|
+
sources: [...sources].sort(),
|
|
147
|
+
engines: [...engines].sort((a, b) => a - b),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const jaccard = (a, b) => {
|
|
151
|
+
if (a.length === 0 && b.length === 0)
|
|
152
|
+
return 1;
|
|
153
|
+
const setB = new Set(b);
|
|
154
|
+
let shared = 0;
|
|
155
|
+
for (const x of new Set(a))
|
|
156
|
+
if (setB.has(x))
|
|
157
|
+
shared += 1;
|
|
158
|
+
const union = new Set([...a, ...b]).size;
|
|
159
|
+
return union === 0 ? 1 : shared / union;
|
|
160
|
+
};
|
|
161
|
+
/** Groove similarity ignores which cell a rhythm sat on — the same pattern
|
|
162
|
+
* moved from cell 3 to cell 5 is still the same pattern. */
|
|
163
|
+
const grooveSimilarity = (a, b) => jaccard(a.map((g) => g.split(":")[1] ?? ""), b.map((g) => g.split(":")[1] ?? ""));
|
|
164
|
+
const modSimilarity = (a, b) => {
|
|
165
|
+
const density = (m) => m.lfo + m.env + m.vary + m.seq;
|
|
166
|
+
const da = density(a);
|
|
167
|
+
const db = density(b);
|
|
168
|
+
const scale = Math.max(da, db) || 1;
|
|
169
|
+
const densityMatch = 1 - Math.abs(da - db) / scale;
|
|
170
|
+
// What is modulated matters more than how much of it there is.
|
|
171
|
+
return 0.35 * densityMatch + 0.65 * jaccard(a.targets, b.targets);
|
|
172
|
+
};
|
|
173
|
+
/** Dimension weights. Groove and params dominate because they are what a
|
|
174
|
+
* listener names first when two presets "sound the same". */
|
|
175
|
+
const WEIGHTS = { params: 0.24, groove: 0.24, mod: 0.18, sources: 0.16, fx: 0.08, chains: 0.05, pitch: 0.05 };
|
|
176
|
+
export function similarity(a, b) {
|
|
177
|
+
const byDimension = {
|
|
178
|
+
params: jaccard(a.params, b.params),
|
|
179
|
+
groove: grooveSimilarity(a.groove, b.groove),
|
|
180
|
+
mod: modSimilarity(a.mod, b.mod),
|
|
181
|
+
sources: jaccard(a.sources, b.sources),
|
|
182
|
+
fx: jaccard(a.fx, b.fx),
|
|
183
|
+
chains: jaccard(a.chains, b.chains),
|
|
184
|
+
pitch: jaccard(a.pitch.map(String), b.pitch.map(String)),
|
|
185
|
+
};
|
|
186
|
+
let overall = 0;
|
|
187
|
+
for (const [dim, w] of Object.entries(WEIGHTS)) {
|
|
188
|
+
overall += w * byDimension[dim];
|
|
189
|
+
}
|
|
190
|
+
return { overall, byDimension };
|
|
191
|
+
}
|
|
192
|
+
/** Score a candidate against a corpus. novelty = 1 - closest match, so 0 means
|
|
193
|
+
* "this already exists" and 1 means "nothing like it has shipped". */
|
|
194
|
+
export function scoreNovelty(candidate, corpus, topN = 5, baseline) {
|
|
195
|
+
const scored = corpus
|
|
196
|
+
.map((other) => ({ other, sim: similarity(candidate, other) }))
|
|
197
|
+
.sort((x, y) => y.sim.overall - x.sim.overall)
|
|
198
|
+
.slice(0, topN);
|
|
199
|
+
const nearest = scored.map(({ other, sim }) => ({
|
|
200
|
+
name: other.name,
|
|
201
|
+
similarity: Math.round(sim.overall * 1000) / 1000,
|
|
202
|
+
// Dimensions where the candidate is essentially a copy. These are the
|
|
203
|
+
// actionable part of the report — they name what to change.
|
|
204
|
+
duplicatedDimensions: Object.entries(sim.byDimension)
|
|
205
|
+
.filter(([, v]) => v >= 0.7)
|
|
206
|
+
.sort((a, b) => b[1] - a[1])
|
|
207
|
+
.map(([k, v]) => `${k} ${Math.round(v * 100)}%`),
|
|
208
|
+
}));
|
|
209
|
+
const top = scored[0]?.sim.overall ?? 0;
|
|
210
|
+
const novelty = Math.round((1 - top) * 1000) / 1000;
|
|
211
|
+
let verdict;
|
|
212
|
+
let percentileVsCorpus = null;
|
|
213
|
+
if (baseline) {
|
|
214
|
+
// Linear interpolation across the three known percentiles is enough for a
|
|
215
|
+
// verdict — this is a calibration aid, not a statistic.
|
|
216
|
+
if (novelty <= baseline.p10)
|
|
217
|
+
percentileVsCorpus = Math.max(0, Math.round((10 * novelty) / baseline.p10));
|
|
218
|
+
else if (novelty <= baseline.p50)
|
|
219
|
+
percentileVsCorpus = Math.round(10 + (40 * (novelty - baseline.p10)) / (baseline.p50 - baseline.p10));
|
|
220
|
+
else if (novelty <= baseline.p90)
|
|
221
|
+
percentileVsCorpus = Math.round(50 + (40 * (novelty - baseline.p50)) / (baseline.p90 - baseline.p50));
|
|
222
|
+
else
|
|
223
|
+
percentileVsCorpus = Math.min(100, Math.round(90 + (10 * (novelty - baseline.p90)) / (1 - baseline.p90)));
|
|
224
|
+
verdict =
|
|
225
|
+
novelty < baseline.floor
|
|
226
|
+
? `More derivative than ANY pair in the shipped corpus (the closest shipped pair scores ${baseline.floor}). Redesign the duplicated dimensions before saving.`
|
|
227
|
+
: novelty < baseline.p10
|
|
228
|
+
? "More derivative than 90% of shipped presets. Change at least one duplicated dimension."
|
|
229
|
+
: novelty < baseline.p50
|
|
230
|
+
? "Below the corpus median for distinctness. Acceptable, but the duplicated dimensions are where it blends in."
|
|
231
|
+
: novelty < baseline.p90
|
|
232
|
+
? "Distinct at or above the corpus median."
|
|
233
|
+
: "More distinct than 90% of shipped presets.";
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
verdict = `Nearest-neighbour similarity ${Math.round(top * 100)}%. No corpus baseline supplied, so this number is only comparable against other runs.`;
|
|
237
|
+
}
|
|
238
|
+
return { novelty, percentileVsCorpus, nearest, verdict };
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../src/fingerprint.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,wDAAwD;AACxD,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,4BAA4B;AAyB5B,MAAM,OAAO,GAAG,CAAC,CAAU,EAAa,EAAE;IACxC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,CAAU,EAA2B,EAAE;IACvD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1E,CAAC,CAAC;AAEF;;4DAE4D;AAC5D,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE,CAC/C,IAAI;KACD,KAAK,CAAC,OAAO,CAAC;KACd,GAAG,EAAG;KACN,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;KAC5B,WAAW,EAAE;KACb,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;KACzB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;KAC1B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAElB;yDACyD;AACzD,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAiB,EAAE,CACvD,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAErG,MAAM,UAAU,gBAAgB,CAC9B,KAA8B,EAC9B,QAAgC,EAChC,WAA8B,EAC9B,IAAI,GAAG,WAAW;IAElB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,EAAE,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;QACpD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/B,KAAK,IAAI,CAAC,CAAC;QAEX,qEAAqE;QACrE,wEAAwE;QACxE,yEAAyE;QACzE,8BAA8B;QAC9B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;gBAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrD,CAAC;QAED,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAS;YACxE,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7B,iEAAiE;YACjE,uBAAuB;YACvB,IAAI,GAAG,KAAK,SAAS,IAAI,KAAK,KAAK,GAAG;gBAAE,SAAS;YACjD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QACvG,IAAI,KAAK,CAAC,MAAM;YAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAE9C,0EAA0E;QAC1E,sEAAsE;QACtE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,MAAM,CAAC,GAAI,EAAyB,CAAC,IAAI,CAAC;YAC1C,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,KAAK,CAAC,IAAI;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEnF,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtD,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC;YAC1D,MAAM,CAAC,GAAI,EAA0B,CAAC,KAAK,CAAC;YAC5C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAU,EAAE;QACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,YAAY,CAAE,CAA2B,CAAC,OAAO,CAAC,CAAC;YAC/D,IAAI,GAAG;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC;IACtB,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,EAAE,GAAI,KAAgC,CAAC,QAAQ,CAAC;QACtD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC;IAE1D,OAAO;QACL,IAAI;QACJ,GAAG,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5C,KAAK;QACL,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE;QAC1B,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE;QAC1B,GAAG,EAAE;YACH,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC;YACvB,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC;YAC5B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;YACxB,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC;YAC5B,OAAO,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE;SAChC;QACD,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;QACrB,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE;QAC5B,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,CAAW,EAAE,CAAW,EAAU,EAAE;IACnD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;QAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;AAC1C,CAAC,CAAC;AAEF;6DAC6D;AAC7D,MAAM,gBAAgB,GAAG,CAAC,CAAW,EAAE,CAAW,EAAU,EAAE,CAC5D,OAAO,CACL,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EACnC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CACpC,CAAC;AAEJ,MAAM,aAAa,GAAG,CAAC,CAAqB,EAAE,CAAqB,EAAU,EAAE;IAC7E,MAAM,OAAO,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC;IAC1E,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC;IACnD,+DAA+D;IAC/D,OAAO,IAAI,GAAG,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;AACpE,CAAC,CAAC;AAEF;8DAC8D;AAC9D,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAO9G,MAAM,UAAU,UAAU,CAAC,CAAc,EAAE,CAAc;IACvD,MAAM,WAAW,GAAG;QAClB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACnC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QAC5C,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;QAChC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC;QACtC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QACvB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;KACzD,CAAC;IACF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC,GAAG,WAAW,CAAC,GAA2B,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAClC,CAAC;AAgBD;uEACuE;AACvE,MAAM,UAAU,YAAY,CAC1B,SAAsB,EACtB,MAAqB,EACrB,IAAI,GAAG,CAAC,EACR,QAA0B;IAE1B,MAAM,MAAM,GAAG,MAAM;SAClB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;SAC9D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;SAC7C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI;QACjD,sEAAsE;QACtE,4DAA4D;QAC5D,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;aAClD,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC;aAC3B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;KACnD,CAAC,CAAC,CAAC;IAEJ,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpD,IAAI,OAAe,CAAC;IACpB,IAAI,kBAAkB,GAAkB,IAAI,CAAC;IAC7C,IAAI,QAAQ,EAAE,CAAC;QACb,0EAA0E;QAC1E,wDAAwD;QACxD,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG;YAAE,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;aACpG,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG;YAC9B,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;aACnG,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG;YAC9B,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;;YACnG,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAE/G,OAAO;YACL,OAAO,GAAG,QAAQ,CAAC,KAAK;gBACtB,CAAC,CAAC,wFAAwF,QAAQ,CAAC,KAAK,sDAAsD;gBAC9J,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG;oBACtB,CAAC,CAAC,wFAAwF;oBAC1F,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG;wBACtB,CAAC,CAAC,6GAA6G;wBAC/G,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG;4BACtB,CAAC,CAAC,yCAAyC;4BAC3C,CAAC,CAAC,4CAA4C,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,gCAAgC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,uFAAuF,CAAC;IACzJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface VariationSnapshotContract {
|
|
2
|
+
/** Session keys a variation must not carry (shared/variationSessionKeys.json). */
|
|
3
|
+
sessionKeys: readonly string[];
|
|
4
|
+
/** Visual audio-react paramIds — they persist ACROSS variations, so they are
|
|
5
|
+
* stripped from every snapshot rather than captured per slot. */
|
|
6
|
+
visualParamIds: readonly string[];
|
|
7
|
+
/** Number of shared LFO generators (NUM_LFOS). */
|
|
8
|
+
lfoGeneratorCount: number;
|
|
9
|
+
}
|
|
10
|
+
export interface VariationSnapshotOptions {
|
|
11
|
+
/** FX-router products ship without Vary — never serialize its matrix state. */
|
|
12
|
+
isFxProduct?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Force each `lfo{g}_enabled` flag to match whether that generator still has a
|
|
16
|
+
* live link.
|
|
17
|
+
*
|
|
18
|
+
* A generator whose links were all removed or disabled must not persist as
|
|
19
|
+
* enabled: on load it would re-arm a generator driving nothing, and the next
|
|
20
|
+
* link added to that slot would inherit an on state the user never set.
|
|
21
|
+
*/
|
|
22
|
+
export declare function reconcileLfoGeneratorEnabledState(snapshot: Record<string, unknown>, lfoGeneratorCount: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Shape a variation snapshot from plugin state.
|
|
25
|
+
*
|
|
26
|
+
* Order matters and is part of the contract: strip session keys, drop the
|
|
27
|
+
* active-preset path, strip visual params, narrow for FX products, then
|
|
28
|
+
* reconcile the LFO generator flags LAST — the reconcile reads `lfos`, which
|
|
29
|
+
* the earlier steps may have removed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildVariationSnapshotBase(state: Record<string, unknown>, contract: VariationSnapshotContract, options?: VariationSnapshotOptions): Record<string, unknown>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// GENERATED COPY of shared/variationSnapshot.ts — do not edit here.
|
|
2
|
+
// Regenerate: node scripts/generate-variation-session-keys.mjs
|
|
3
|
+
// The variation-snapshot shaping both save paths run.
|
|
4
|
+
//
|
|
5
|
+
// The UI (interface/src/interop.ts) and MCP (packages/mcp-server) both shape
|
|
6
|
+
// through here, because two implementations drift: the MCP side was missing 16
|
|
7
|
+
// session keys, the visual-param strip and the LFO-generator reconcile, so a
|
|
8
|
+
// variation saved over MCP was not the same bytes as one saved from the window.
|
|
9
|
+
//
|
|
10
|
+
// This is the PURE half. It takes state plus the contract data and returns the
|
|
11
|
+
// shaped snapshot, so both callers produce identical bytes for identical input
|
|
12
|
+
// (pinned by interface/test/variationSnapshotParity.test.ts).
|
|
13
|
+
//
|
|
14
|
+
// The IMPURE half stays per-caller and is genuinely different: the UI can await
|
|
15
|
+
// `refreshLoadedPluginStateBlobs()` and stamp hosted-plugin identity/bypass from
|
|
16
|
+
// `pluginHostStore`; the headless bridge cannot reach either, and says so on the
|
|
17
|
+
// save result instead of writing a silently lossy slot.
|
|
18
|
+
//
|
|
19
|
+
// GENERATED COPY: packages/mcp-server/src/generated/variationSnapshot.ts is
|
|
20
|
+
// produced from this file by scripts/generate-variation-session-keys.mjs.
|
|
21
|
+
// Edit THIS file; the drift check fails if the copy is stale.
|
|
22
|
+
/**
|
|
23
|
+
* Force each `lfo{g}_enabled` flag to match whether that generator still has a
|
|
24
|
+
* live link.
|
|
25
|
+
*
|
|
26
|
+
* A generator whose links were all removed or disabled must not persist as
|
|
27
|
+
* enabled: on load it would re-arm a generator driving nothing, and the next
|
|
28
|
+
* link added to that slot would inherit an on state the user never set.
|
|
29
|
+
*/
|
|
30
|
+
export function reconcileLfoGeneratorEnabledState(snapshot, lfoGeneratorCount) {
|
|
31
|
+
if (!Array.isArray(snapshot.lfos))
|
|
32
|
+
return;
|
|
33
|
+
const lfos = snapshot.lfos;
|
|
34
|
+
for (let generatorIndex = 1; generatorIndex <= lfoGeneratorCount; generatorIndex++) {
|
|
35
|
+
const hasEnabledLinks = lfos.some((lfo) => lfo?.lfoGeneratorIndex === generatorIndex &&
|
|
36
|
+
(lfo?.enabled ?? 1) === 1 &&
|
|
37
|
+
typeof lfo?.paramId === 'string' &&
|
|
38
|
+
lfo.paramId.length > 0);
|
|
39
|
+
snapshot[`lfo${generatorIndex}_enabled`] = hasEnabledLinks ? 1 : 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Shape a variation snapshot from plugin state.
|
|
44
|
+
*
|
|
45
|
+
* Order matters and is part of the contract: strip session keys, drop the
|
|
46
|
+
* active-preset path, strip visual params, narrow for FX products, then
|
|
47
|
+
* reconcile the LFO generator flags LAST — the reconcile reads `lfos`, which
|
|
48
|
+
* the earlier steps may have removed.
|
|
49
|
+
*/
|
|
50
|
+
export function buildVariationSnapshotBase(state, contract, options = {}) {
|
|
51
|
+
const snapshot = { ...state };
|
|
52
|
+
for (const key of contract.sessionKeys)
|
|
53
|
+
delete snapshot[key];
|
|
54
|
+
delete snapshot.activePresetPath;
|
|
55
|
+
for (const paramId of contract.visualParamIds)
|
|
56
|
+
delete snapshot[paramId];
|
|
57
|
+
if (options.isFxProduct)
|
|
58
|
+
delete snapshot.vary;
|
|
59
|
+
reconcileLfoGeneratorEnabledState(snapshot, contract.lfoGeneratorCount);
|
|
60
|
+
return snapshot;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=variationSnapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variationSnapshot.js","sourceRoot":"","sources":["../../src/generated/variationSnapshot.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,+DAA+D;AAC/D,sDAAsD;AACtD,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,8DAA8D;AAC9D,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,wDAAwD;AACxD,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,8DAA8D;AAuB9D;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,QAAiC,EACjC,iBAAyB;IAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAM;IACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAiB,CAAA;IAEvC,KAAK,IAAI,cAAc,GAAG,CAAC,EAAE,cAAc,IAAI,iBAAiB,EAAE,cAAc,EAAE,EAAE,CAAC;QACnF,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAC/B,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,EAAE,iBAAiB,KAAK,cAAc;YACzC,CAAC,GAAG,EAAE,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC;YACzB,OAAO,GAAG,EAAE,OAAO,KAAK,QAAQ;YAChC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CACzB,CAAA;QACD,QAAQ,CAAC,MAAM,cAAc,UAAU,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACpE,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAA8B,EAC9B,QAAmC,EACnC,UAAoC,EAAE;IAEtC,MAAM,QAAQ,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAA;IAEtD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC5D,OAAO,QAAQ,CAAC,gBAAgB,CAAA;IAChC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,cAAc;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAA;IACvE,IAAI,OAAO,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC,IAAI,CAAA;IAE7C,iCAAiC,CAAC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAA;IACvE,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|