@openmixer/plugin-qualify 0.18.0 → 0.18.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 +84 -2
- package/bin/plugin-qualify-sync.mjs +3 -1
- package/dist/distributed-records.d.ts +4 -2
- package/dist/distributed-records.js +1 -1
- package/dist/distributed-records.js.map +1 -1
- package/dist/host-admission.d.ts +2 -0
- package/dist/host-admission.js +12 -3
- package/dist/host-admission.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/probe-judge.d.ts +11 -0
- package/dist/probe-judge.js +4 -1
- package/dist/probe-judge.js.map +1 -1
- package/dist/probe-runner.js +3 -2
- package/dist/probe-runner.js.map +1 -1
- package/dist/qualification-record.d.ts +90 -8
- package/dist/qualification-record.js +18 -13
- package/dist/qualification-record.js.map +1 -1
- package/dist/qualify-adapter-pass.d.ts +67 -0
- package/dist/qualify-adapter-pass.js +255 -0
- package/dist/qualify-adapter-pass.js.map +1 -0
- package/dist/qualify-declarations.d.ts +25 -0
- package/dist/qualify-declarations.js +17 -1
- package/dist/qualify-declarations.js.map +1 -1
- package/dist/qualify-rejudge.d.ts +81 -0
- package/dist/qualify-rejudge.js +421 -0
- package/dist/qualify-rejudge.js.map +1 -0
- package/dist/qualify-sync.d.ts +72 -1
- package/dist/qualify-sync.js +176 -17
- package/dist/qualify-sync.js.map +1 -1
- package/fixtures/records-0.16.1/http___example.org_raffo@unversioned-2060c09ff626.default.probe.json +46 -0
- package/fixtures/records-0.16.1/http___example.org_raffo@unversioned-2060c09ff626.default.qualification.json +185 -0
- package/fixtures/records-0.16.1/http___lv2plug.in_plugins_eg-amp@unversioned-f046a60efd45.default.probe.json +51 -0
- package/fixtures/records-0.16.1/http___lv2plug.in_plugins_eg-amp@unversioned-f046a60efd45.default.qualification.json +232 -0
- package/fixtures/records-0.16.1/https___github.com_lucianodato_noise-repellent@unversioned-c3b5e21dd9a7.default.probe.json +51 -0
- package/fixtures/records-0.16.1/https___github.com_lucianodato_noise-repellent@unversioned-c3b5e21dd9a7.default.qualification.json +245 -0
- package/fixtures/records-0.16.1/in.lsp-plug.impulse_responses_mono@unversioned-d5235d2f5f53.custom.probe.json +52 -0
- package/fixtures/records-0.16.1/in.lsp-plug.impulse_responses_mono@unversioned-d5235d2f5f53.custom.qualification.json +145 -0
- package/fixtures/records-0.16.1/in.lsp-plug.trigger_midi_mono@unversioned-d5235d2f5f53.custom.probe.json +52 -0
- package/fixtures/records-0.16.1/in.lsp-plug.trigger_midi_mono@unversioned-d5235d2f5f53.custom.qualification.json +155 -0
- package/package.json +1 -1
- package/src/distributed-records.ts +1 -1
- package/src/host-admission.ts +13 -3
- package/src/index.ts +2 -0
- package/src/probe-judge.ts +8 -1
- package/src/probe-runner.ts +2 -2
- package/src/qualification-record.ts +100 -15
- package/src/qualify-adapter-pass.ts +278 -0
- package/src/qualify-declarations.ts +19 -1
- package/src/qualify-rejudge.ts +452 -0
- package/src/qualify-sync.ts +192 -17
- package/tools/lv2_resolve.py +49 -0
- package/tools/mod-host-admit.mjs +21 -5
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
|
+
/**
|
|
4
|
+
* `plugin-qualify-sync --adapter-only`: an LV2 plugin's verdict on omx-clap-host is decided by its run under plugin-hostd's
|
|
5
|
+
* LV2 adapter, and a record without one owes it (`qualify-rejudge.ts`). This pass makes only that run, for each plugin, at
|
|
6
|
+
* each cell a Pi runs, and puts the block in the record; every other figure stays as it was, and `--rejudge` reads it.
|
|
7
|
+
*
|
|
8
|
+
* Each cell has a private JACK server of its own (`jackd -d dummy`, the rate and quantum on its command line), so that the
|
|
9
|
+
* run can never reach the device's live server (plugin-qualify#187), and each run a fresh host, its time limited by
|
|
10
|
+
* `budget_s` a command and its address space by the limit of a direct spawn. A crash or a timeout is the plugin's recorded
|
|
11
|
+
* outcome. A pass does not start without the versions of the hosts it runs, or without a private server: nothing it could
|
|
12
|
+
* write would say what it measured. A plugin is finished when its block was taken at these host versions at every cell,
|
|
13
|
+
* so a pass cut short can be run again; a host that could not be run decides nothing, and that plugin is tried again.
|
|
14
|
+
*/
|
|
15
|
+
import { spawn, spawnSync, type ChildProcess } from "node:child_process";
|
|
16
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { basename, join } from "node:path";
|
|
19
|
+
|
|
20
|
+
import { ADAPTER_HOST, SPAWNED_ADMISSIONS, admitUnderAdapter, type AdmissionRuns, type HostAdmission } from "./host-admission.js";
|
|
21
|
+
import { HOSTD_ENV } from "./hostd-pin-files.js";
|
|
22
|
+
import { cellKey, type GridCell } from "./measurement-grid.js";
|
|
23
|
+
import { defaultAddressSpaceLimit } from "./direct-limit.js";
|
|
24
|
+
import { PROBE_OK } from "./probe-output.js";
|
|
25
|
+
import { QUALIFICATION_RECORD_SCHEMA, type AdapterBlock, type QualificationRecord } from "./qualification-record.js";
|
|
26
|
+
import { ADMIT_ADDRESS_KB_ENV, ADMIT_STATE_DIR_ENV, ADMIT_STEP_MS_ENV, PI_CELLS } from "./qualify-declarations.js";
|
|
27
|
+
import { digestFiles, type SyncContext, type SyncState } from "./qualify-sync.js";
|
|
28
|
+
import { writeFileWhole } from "./whole-file.js";
|
|
29
|
+
|
|
30
|
+
/** The cells the pass runs at: the ones a Pi runs. */
|
|
31
|
+
export const ADAPTER_PASS_CELLS: readonly GridCell[] = PI_CELLS;
|
|
32
|
+
|
|
33
|
+
/** The code of a cell no private server could be had for: the run did not happen. */
|
|
34
|
+
export const NO_PRIVATE_SERVER = `${ADAPTER_HOST}.no-private-server`;
|
|
35
|
+
|
|
36
|
+
const RECORD_SUFFIX = ".qualification.json";
|
|
37
|
+
const SERVER_WAIT_MS = 5000;
|
|
38
|
+
const SERVER_SETTLE_MS = 300;
|
|
39
|
+
const SERVER_STOP_MS = 2000;
|
|
40
|
+
|
|
41
|
+
/** What a program answers to `--version`: its last word on the line that says `version`. */
|
|
42
|
+
export function parseVersion(text: string): string | undefined {
|
|
43
|
+
return /version:?\s+(\S+)/.exec(text)?.[1];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type VersionRun = (tool: string) => { readonly status: number | null; readonly stdout: string };
|
|
47
|
+
|
|
48
|
+
const spawnVersion: VersionRun = (tool) => {
|
|
49
|
+
const r = spawnSync(tool, ["--version"], { encoding: "utf8", timeout: 10000, killSignal: "SIGKILL" });
|
|
50
|
+
return { status: r.status, stdout: `${r.stdout ?? ""}\n${r.stderr ?? ""}` };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** The versions of the hosts as installed now, or which of them could not say. */
|
|
54
|
+
export function hostVersions(env: Readonly<Record<string, string | undefined>>, run: VersionRun = spawnVersion): { ok: true; versions: Readonly<Record<string, string>> } | { ok: false; error: string } {
|
|
55
|
+
const versions: Record<string, string> = {};
|
|
56
|
+
for (const [name, tool] of [[ADAPTER_HOST, ADAPTER_HOST], ["plugin-hostd", env[HOSTD_ENV] ?? "plugin-hostd"]] as const) {
|
|
57
|
+
const r = run(tool);
|
|
58
|
+
const version = r.status === 0 ? parseVersion(r.stdout) : undefined;
|
|
59
|
+
if (version === undefined) return { ok: false, error: `${tool} --version names no version, so a run could not say which host it was under` };
|
|
60
|
+
versions[name] = version;
|
|
61
|
+
}
|
|
62
|
+
return { ok: true, versions };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A private JACK server for one cell, for as long as a pass needs it. */
|
|
66
|
+
export interface PrivateJack {
|
|
67
|
+
readonly name: string;
|
|
68
|
+
alive(): boolean;
|
|
69
|
+
stop(): void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type JackStarter = (cell: GridCell) => PrivateJack | undefined;
|
|
73
|
+
|
|
74
|
+
const sleepSync = (ms: number): void => {
|
|
75
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** `jackd` on a name of its own and the dummy driver, at the cell's rate and quantum; `undefined` when none came up. */
|
|
79
|
+
export const startPrivateJack: JackStarter = (cell) => {
|
|
80
|
+
const name = `omx-qualify-${String(process.pid)}-${String(cell.quantum)}`;
|
|
81
|
+
let child: ChildProcess;
|
|
82
|
+
let ended = false;
|
|
83
|
+
try {
|
|
84
|
+
child = spawn("jackd", ["-r", "-n", name, "-d", "dummy", "-r", String(cell.rate), "-p", String(cell.quantum)], { stdio: "ignore", detached: true });
|
|
85
|
+
} catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
child.on("error", () => (ended = true));
|
|
89
|
+
child.on("exit", () => (ended = true));
|
|
90
|
+
child.unref();
|
|
91
|
+
const running = (): boolean => {
|
|
92
|
+
if (ended || child.pid === undefined) return false;
|
|
93
|
+
// the exit event needs the loop this synchronous pass holds: the process table says
|
|
94
|
+
return existsSync(`/proc/${String(child.pid)}`) && !/^\S+ \(.*\) Z/.test(readStat(child.pid));
|
|
95
|
+
};
|
|
96
|
+
// a TERM first: jackd frees the shared memory it holds under /dev/shm itself, which a KILL would leave behind for good
|
|
97
|
+
const signal = (sig: NodeJS.Signals): void => {
|
|
98
|
+
try {
|
|
99
|
+
if (child.pid !== undefined) process.kill(-child.pid, sig);
|
|
100
|
+
} catch {
|
|
101
|
+
// already gone
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const stop = (): void => {
|
|
105
|
+
signal("SIGTERM");
|
|
106
|
+
const until = Date.now() + SERVER_STOP_MS;
|
|
107
|
+
while (running() && Date.now() < until) sleepSync(20);
|
|
108
|
+
if (running()) signal("SIGKILL");
|
|
109
|
+
for (const d of serverDirs(name)) rmSync(d, { recursive: true, force: true });
|
|
110
|
+
};
|
|
111
|
+
const until = Date.now() + SERVER_WAIT_MS;
|
|
112
|
+
while (Date.now() < until) {
|
|
113
|
+
if (child.pid === undefined || !running()) {
|
|
114
|
+
stop();
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
if (serverDirs(name).length > 0) {
|
|
118
|
+
sleepSync(SERVER_SETTLE_MS);
|
|
119
|
+
return { name, alive: running, stop };
|
|
120
|
+
}
|
|
121
|
+
sleepSync(50);
|
|
122
|
+
}
|
|
123
|
+
stop();
|
|
124
|
+
return undefined;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const readStat = (pid: number): string => {
|
|
128
|
+
try {
|
|
129
|
+
return readFileSync(`/proc/${String(pid)}/stat`, "utf8");
|
|
130
|
+
} catch {
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** Where jackd keeps a server's sockets, and its semaphores: the socket appears as the server opens it. */
|
|
136
|
+
function serverDirs(name: string): string[] {
|
|
137
|
+
try {
|
|
138
|
+
return readdirSync("/dev/shm").filter((d) => d.startsWith(`jack_${name}_`) || new RegExp(`^jack_sem\\.\\d+_${name}_`).test(d)).map((d) => join("/dev/shm", d));
|
|
139
|
+
} catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Whether a machine can give a pass its private server at all: the program is there. */
|
|
145
|
+
function jackdInstalled(env: Readonly<Record<string, string | undefined>>): boolean {
|
|
146
|
+
return (env["PATH"] ?? process.env["PATH"] ?? "").split(":").some((d) => d !== "" && existsSync(join(d, "jackd")));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// --- the block --------------------------------------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The runs of every cell as one block. Its top is what the judge reads: a refusal or a crash at any cell decides, with the
|
|
153
|
+
* cell it happened at, whatever the others say; then a cell where the host could not be run leaves it not measured, so
|
|
154
|
+
* still owed; only every cell admitted is admitted. Pure.
|
|
155
|
+
*/
|
|
156
|
+
export function adapterBlockOf(runs: readonly { readonly cell: GridCell; readonly run: HostAdmission }[], versions: Readonly<Record<string, string>>, measuredAt: string): AdapterBlock {
|
|
157
|
+
const cells = runs.map(({ cell, run }) => ({ rate: cell.rate, quantum: cell.quantum, measured: run.measured, admitted: run.admitted, ...(run.code === undefined ? {} : { code: run.code }), params: run.params }));
|
|
158
|
+
const worst = cells.find((c) => c.measured && !c.admitted) ?? cells.find((c) => !c.measured);
|
|
159
|
+
const top =
|
|
160
|
+
worst === undefined
|
|
161
|
+
? { measured: true, admitted: true, params: {} }
|
|
162
|
+
: { measured: worst.measured, admitted: false, ...(worst.code === undefined ? {} : { code: worst.code }), params: { ...worst.params, rate: worst.rate, quantum: worst.quantum } };
|
|
163
|
+
return { host: ADAPTER_HOST, ...top, versions, measuredAt, cells };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The record with the block, and every other byte of it as it was. Pure. */
|
|
167
|
+
export const mergeAdapterRun = (record: QualificationRecord, block: AdapterBlock): QualificationRecord => ({ ...record, adapter: block });
|
|
168
|
+
|
|
169
|
+
/** Whether a record's adapter run is done: at these host versions, measured, at every cell. */
|
|
170
|
+
export function adapterFinished(record: QualificationRecord, versions: Readonly<Record<string, string>>, cells: readonly GridCell[] = ADAPTER_PASS_CELLS): boolean {
|
|
171
|
+
const a = record.adapter;
|
|
172
|
+
if (a === undefined || !a.measured || a.versions === undefined || a.cells === undefined) return false;
|
|
173
|
+
const same = Object.keys(versions).length === Object.keys(a.versions).length && Object.entries(versions).every(([k, v]) => a.versions![k] === v);
|
|
174
|
+
return same && cells.every((c) => a.cells!.some((x) => cellKey(x) === cellKey(c)));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// --- the pass ---------------------------------------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
export interface AdapterPassOptions {
|
|
180
|
+
readonly force?: boolean;
|
|
181
|
+
readonly runs?: AdmissionRuns;
|
|
182
|
+
readonly jack?: JackStarter;
|
|
183
|
+
readonly versions?: VersionRun;
|
|
184
|
+
/** The test's stand-in for jackd says it is there; the real pass looks. */
|
|
185
|
+
readonly jackdPresent?: boolean;
|
|
186
|
+
readonly now?: () => string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface AdapterPassResult {
|
|
190
|
+
readonly measured: number;
|
|
191
|
+
readonly already: number;
|
|
192
|
+
readonly failed: number;
|
|
193
|
+
readonly versions: Readonly<Record<string, string>>;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** The pass could not start: nothing was run and nothing written. */
|
|
197
|
+
export class AdapterPassRefused extends Error {}
|
|
198
|
+
|
|
199
|
+
const readRecord = (path: string): QualificationRecord | undefined => {
|
|
200
|
+
try {
|
|
201
|
+
const r = JSON.parse(readFileSync(path, "utf8")) as QualificationRecord;
|
|
202
|
+
return r.schema === QUALIFICATION_RECORD_SCHEMA ? r : undefined;
|
|
203
|
+
} catch {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The run of each LV2 plugin of the state whose probe ended well (one that did not has no verdict a run could decide), at
|
|
210
|
+
* each cell. Each record is written whole as its plugin ends.
|
|
211
|
+
*/
|
|
212
|
+
export function adapterPass(ctx: SyncContext, state: SyncState, options: AdapterPassOptions = {}): AdapterPassResult {
|
|
213
|
+
const read = hostVersions(ctx.env, options.versions);
|
|
214
|
+
if (!read.ok) throw new AdapterPassRefused(read.error);
|
|
215
|
+
if (!(options.jackdPresent ?? jackdInstalled(ctx.env))) throw new AdapterPassRefused(`no jackd on the path: ${NO_PRIVATE_SERVER}, and the device's own JACK server is never used for this`);
|
|
216
|
+
const { versions } = read;
|
|
217
|
+
const starter = options.jack ?? startPrivateJack;
|
|
218
|
+
const runs = options.runs ?? SPAWNED_ADMISSIONS;
|
|
219
|
+
const now = options.now ?? (() => new Date().toISOString());
|
|
220
|
+
const servers = new Map<string, PrivateJack>();
|
|
221
|
+
const serverFor = (cell: GridCell): PrivateJack | undefined => {
|
|
222
|
+
const key = cellKey(cell);
|
|
223
|
+
const had = servers.get(key);
|
|
224
|
+
if (had?.alive() === true) return had;
|
|
225
|
+
had?.stop();
|
|
226
|
+
servers.delete(key);
|
|
227
|
+
const fresh = starter(cell);
|
|
228
|
+
if (fresh !== undefined) servers.set(key, fresh);
|
|
229
|
+
return fresh;
|
|
230
|
+
};
|
|
231
|
+
const count = { measured: 0, already: 0, failed: 0 };
|
|
232
|
+
const limit = { [ADMIT_STEP_MS_ENV]: String(ctx.settings.budgetS * 1000), [ADMIT_ADDRESS_KB_ENV]: String(Math.ceil(defaultAddressSpaceLimit() / 2 ** 10)) };
|
|
233
|
+
try {
|
|
234
|
+
for (const [key, entry] of Object.entries(state.entries)) {
|
|
235
|
+
if (entry.format !== "lv2") continue;
|
|
236
|
+
const files = digestFiles(entry.root, entry.files.map((f) => f.path), entry.files);
|
|
237
|
+
if (files === undefined || !files.every((f) => entry.files.some((x) => x.path === f.path && x.sha256 === f.sha256))) {
|
|
238
|
+
ctx.log(`${key}: its files changed since it was measured; the sync measures it whole`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
for (const plugin of entry.plugins) {
|
|
242
|
+
if (plugin.record === undefined || !plugin.record.endsWith(RECORD_SUFFIX)) continue;
|
|
243
|
+
const path = join(ctx.settings.qualificationDir, basename(plugin.record));
|
|
244
|
+
const record = readRecord(path);
|
|
245
|
+
if (record === undefined) {
|
|
246
|
+
ctx.log(`${plugin.uri}: its record does not read; no adapter run`);
|
|
247
|
+
count.failed++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (record.probe.outcome !== PROBE_OK) continue;
|
|
251
|
+
if (options.force !== true && adapterFinished(record, versions)) {
|
|
252
|
+
count.already++;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const started = Date.now();
|
|
256
|
+
const done = ADAPTER_PASS_CELLS.map((cell) => {
|
|
257
|
+
const server = serverFor(cell);
|
|
258
|
+
if (server === undefined) return { cell, run: { host: ADAPTER_HOST, measured: false, admitted: false, code: NO_PRIVATE_SERVER, params: {} } as HostAdmission };
|
|
259
|
+
const stateDir = mkdtempSync(join(tmpdir(), "plugin-qualify-adapter-state-"));
|
|
260
|
+
try {
|
|
261
|
+
const env = { ...ctx.env, ...limit, [ADMIT_STATE_DIR_ENV]: stateDir, LV2_PATH: ctx.settings.lv2Dirs.join(":"), JACK_DEFAULT_SERVER: server.name, JACK_NO_START_SERVER: "1" };
|
|
262
|
+
return { cell, run: admitUnderAdapter(record.plugin.id, runs, env) ?? ({ host: ADAPTER_HOST, measured: false, admitted: false, code: `${ADAPTER_HOST}.admission-failed`, params: {} } as HostAdmission) };
|
|
263
|
+
} finally {
|
|
264
|
+
rmSync(stateDir, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
const block = adapterBlockOf(done, versions, now());
|
|
268
|
+
writeFileWhole(path, `${JSON.stringify(mergeAdapterRun(record, block), null, 2)}\n`);
|
|
269
|
+
if (block.measured) count.measured++;
|
|
270
|
+
else count.failed++;
|
|
271
|
+
ctx.log(`${plugin.uri}: adapter ${block.measured ? (block.admitted ? "admitted" : `refused (${block.code ?? ""})`) : `not run (${block.code ?? ""})`} in ${((Date.now() - started) / 1000).toFixed(1)} s`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
} finally {
|
|
275
|
+
for (const s of servers.values()) s.stop();
|
|
276
|
+
}
|
|
277
|
+
return { ...count, versions };
|
|
278
|
+
}
|
|
@@ -156,6 +156,24 @@ export const ADDRESS_SPACE_LIMIT_RAM_FRACTION = 1;
|
|
|
156
156
|
export const MEMORY_BOUND_EXIT_ENV = "OMX_RT_OOM_EXIT";
|
|
157
157
|
export const MEMORY_BOUND_EXIT_STATUS = 121;
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* The memory-only pass (`plugin-qualify-sync --memory-only`). With `PROBE_MEMORY_ONLY_ENV` set the probers take the
|
|
161
|
+
* footprint and nothing else; the probe runner passes its environment on, so its limits hold unchanged.
|
|
162
|
+
* `PROBE_NOTES_ENV` is the count of notes an instrument that declares no voice capacity holds, basis `probe`:
|
|
163
|
+
* `MEMORY_PASS_PROBE_NOTES` unless `memory_notes` says otherwise.
|
|
164
|
+
*/
|
|
165
|
+
export const PROBE_MEMORY_ONLY_ENV = "OMX_PROBE_MEMORY_ONLY";
|
|
166
|
+
export const PROBE_NOTES_ENV = "OMX_PROBE_NOTES";
|
|
167
|
+
export const MEMORY_PASS_PROBE_NOTES = 16;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* What `tools/mod-host-admit.mjs` is told in an adapter-only pass: how long the host has for one command, its address
|
|
171
|
+
* space (`ulimit -v`, in KB) and the directory for the state files of the plugin it loads.
|
|
172
|
+
*/
|
|
173
|
+
export const ADMIT_STEP_MS_ENV = "OMX_ADMIT_STEP_MS";
|
|
174
|
+
export const ADMIT_ADDRESS_KB_ENV = "OMX_ADMIT_ADDRESS_KB";
|
|
175
|
+
export const ADMIT_STATE_DIR_ENV = "OMX_ADMIT_STATE_DIR";
|
|
176
|
+
|
|
159
177
|
/** Seconds one `.clap` may take to qualify at every rate before the prober is killed. */
|
|
160
178
|
export const CLAP_QUALIFY_TIMEOUT_S = 120;
|
|
161
179
|
|
|
@@ -286,7 +304,7 @@ const piMemory = (ramGib: number): NonNullable<HostBudget["memory"]> => ({
|
|
|
286
304
|
* the other kits, the one a Pi 5 with no kit card also ran its stress and first boot at. The Zynthian benchmark's matrix sweeps
|
|
287
305
|
* more cells; these two are what a Pi is set to.
|
|
288
306
|
*/
|
|
289
|
-
const PI_CELLS = [
|
|
307
|
+
export const PI_CELLS = [
|
|
290
308
|
{ rate: MEASURE_BASE_RATE, quantum: 128 },
|
|
291
309
|
{ rate: MEASURE_BASE_RATE, quantum: 256 },
|
|
292
310
|
];
|