@openmixer/plugin-qualify 0.18.1 → 0.18.2
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 +9 -1
- package/bin/plugin-qualify-sync.mjs +1 -1
- package/dist/clap-qualify.d.ts +3 -1
- package/dist/clap-qualify.js +3 -1
- package/dist/clap-qualify.js.map +1 -1
- package/dist/host-admission.d.ts +19 -0
- package/dist/host-admission.js +21 -0
- package/dist/host-admission.js.map +1 -1
- package/dist/hostd-pin-files.js +3 -1
- package/dist/hostd-pin-files.js.map +1 -1
- package/dist/measurement/format.d.ts +10 -0
- package/dist/measurement/format.js +8 -0
- package/dist/measurement/format.js.map +1 -1
- package/dist/measurement/probe-json.js +2 -1
- package/dist/measurement/probe-json.js.map +1 -1
- package/dist/probe-judge.js +2 -2
- package/dist/probe-judge.js.map +1 -1
- package/dist/qualification-record.d.ts +25 -2
- package/dist/qualification-record.js.map +1 -1
- package/dist/qualify-declarations.d.ts +5 -0
- package/dist/qualify-declarations.js +7 -0
- package/dist/qualify-declarations.js.map +1 -1
- package/dist/qualify-isolated.d.ts +70 -0
- package/dist/qualify-isolated.js +184 -0
- package/dist/qualify-isolated.js.map +1 -0
- package/dist/qualify-rejudge.d.ts +1 -0
- package/dist/qualify-rejudge.js +16 -3
- package/dist/qualify-rejudge.js.map +1 -1
- package/dist/qualify-sync.d.ts +25 -0
- package/dist/qualify-sync.js +104 -11
- package/dist/qualify-sync.js.map +1 -1
- package/package.json +1 -1
- package/src/clap-qualify.ts +5 -1
- package/src/host-admission.ts +27 -0
- package/src/hostd-pin-files.ts +3 -1
- package/src/measurement/format.ts +17 -0
- package/src/measurement/probe-json.ts +2 -1
- package/src/probe-judge.ts +2 -2
- package/src/qualification-record.ts +28 -2
- package/src/qualify-declarations.ts +7 -0
- package/src/qualify-isolated.ts +201 -0
- package/src/qualify-rejudge.ts +17 -3
- package/src/qualify-sync.ts +107 -11
- package/tools/isolated-step.mjs +122 -0
- package/tools/mod-host-admit.mjs +78 -2
package/tools/mod-host-admit.mjs
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
// One mod-host admission: a fresh `mod-host -n` on its own port, then `add` and `remove` of one plugin on its
|
|
5
5
|
// socket. Prints one JSON line, {installed, steps:[{cmd, resp | died}]}, for src/host-admission.ts to classify. The host
|
|
6
6
|
// is always killed at the end, so the next plugin gets a fresh one.
|
|
7
|
-
import { spawn } from "node:child_process";
|
|
7
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
8
9
|
import { createConnection } from "node:net";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
9
11
|
|
|
10
12
|
const uri = process.argv[2];
|
|
11
13
|
// the host to admit under: mod-host, or omx-clap-host for plugin-hostd's LV2 adapter, which answers the same protocol
|
|
@@ -16,6 +18,10 @@ const STEP_MS = Number(process.env.OMX_ADMIT_STEP_MS ?? 0) || 10000;
|
|
|
16
18
|
const ADDRESS_KB = process.env.OMX_ADMIT_ADDRESS_KB;
|
|
17
19
|
const STATE_DIR = process.env.OMX_ADMIT_STATE_DIR;
|
|
18
20
|
const INSTANCE = 0;
|
|
21
|
+
// an instrument under the adapter is played: one note, held this long, its output read at its JACK ports
|
|
22
|
+
const NOTE_WINDOW_MS = 1000;
|
|
23
|
+
const NOTE_ON = `note_on ${INSTANCE} 0 60 100`;
|
|
24
|
+
const ADAPTER = "omx-clap-host";
|
|
19
25
|
const COMMANDS = [
|
|
20
26
|
["add", `add ${uri} ${INSTANCE}`],
|
|
21
27
|
["remove", `remove ${INSTANCE}`],
|
|
@@ -49,6 +55,52 @@ async function connect() {
|
|
|
49
55
|
return undefined;
|
|
50
56
|
}
|
|
51
57
|
|
|
58
|
+
// the LV2 class says it is an instrument; the note port and the audio input say nothing, a synth can have both, an effect
|
|
59
|
+
// can have both. omx-clap-scan comes with omx-clap-host: without an answer from it this is not known to be an instrument
|
|
60
|
+
function isInstrument() {
|
|
61
|
+
const r = spawnSync("omx-clap-scan", ["--fields", `lv2:${uri}`], { encoding: "utf8", timeout: STEP_MS });
|
|
62
|
+
return r.status === 0 && /^plugin\trdf:type\tlv2:InstrumentPlugin$/m.test(r.stdout);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// the JACK output ports of the instance, from audio_ports's reply: resp 0 [dry] in:... out:main:1:2:stereo:"out":out_1,out_2
|
|
66
|
+
function outputPorts(reply) {
|
|
67
|
+
return [...reply.matchAll(/ out:[^ "]*"[^"]*":([^ ]+)/g)].flatMap((m) => m[1].split(",")).map((p) => `effect_${INSTANCE}:${p}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const PEAK_TOOL_DIRS = [fileURLToPath(new URL("../native/build/", import.meta.url)), "/usr/libexec/plugin-qualify/", "/usr/lib/plugin-qualify/"];
|
|
71
|
+
const peakTool = () => process.env.OMX_JACK_PEAK || PEAK_TOOL_DIRS.map((d) => `${d}jack_peak`).find((p) => existsSync(p));
|
|
72
|
+
|
|
73
|
+
// the peak on `ports` while `during` runs: { peak } in linear magnitude, or { problem } with why
|
|
74
|
+
async function peakDuring(ports, during) {
|
|
75
|
+
const tool = peakTool();
|
|
76
|
+
if (tool === undefined) return { problem: "no jack_peak" };
|
|
77
|
+
const meter = spawn(tool, ports, { stdio: ["pipe", "pipe", "pipe"] });
|
|
78
|
+
let out = "";
|
|
79
|
+
let err = "";
|
|
80
|
+
const ended = new Promise((resolve) => {
|
|
81
|
+
meter.on("error", (e) => resolve(e.code ?? "error"));
|
|
82
|
+
meter.on("exit", (code) => resolve(code));
|
|
83
|
+
});
|
|
84
|
+
meter.stdout.on("data", (d) => (out += d.toString()));
|
|
85
|
+
meter.stderr.on("data", (d) => (err += d.toString()));
|
|
86
|
+
const until = Date.now() + STEP_MS;
|
|
87
|
+
while (!out.includes("ready") && Date.now() < until && meter.exitCode === null) await sleep(10);
|
|
88
|
+
if (!out.includes("ready")) {
|
|
89
|
+
meter.kill("SIGKILL");
|
|
90
|
+
await ended;
|
|
91
|
+
return { problem: err.trim() || "jack_peak did not start" };
|
|
92
|
+
}
|
|
93
|
+
await during();
|
|
94
|
+
meter.stdin.end();
|
|
95
|
+
await Promise.race([ended, sleep(STEP_MS)]);
|
|
96
|
+
meter.kill("SIGKILL");
|
|
97
|
+
try {
|
|
98
|
+
return { peak: JSON.parse(out.slice(out.indexOf("ready") + 5)).peak };
|
|
99
|
+
} catch {
|
|
100
|
+
return { problem: err.trim() || "jack_peak gave no figure" };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
52
104
|
async function main() {
|
|
53
105
|
if ((await Promise.race([spawnFailed, sleep(200).then(() => undefined)])) === "ENOENT" || shellNotFound) return { installed: false, steps: [] };
|
|
54
106
|
const sock = await connect();
|
|
@@ -60,7 +112,31 @@ async function main() {
|
|
|
60
112
|
sock.on("close", () => (closed = true));
|
|
61
113
|
sock.on("error", () => (closed = true));
|
|
62
114
|
const steps = [];
|
|
115
|
+
let sound;
|
|
116
|
+
// one line on the socket and its whole answer: undefined when none came in time
|
|
117
|
+
const ask = async (line) => {
|
|
118
|
+
buffer = "";
|
|
119
|
+
sock.write(`${line}\0`);
|
|
120
|
+
const until = Date.now() + STEP_MS;
|
|
121
|
+
while (Date.now() < until && !closed && hostEnd === undefined) {
|
|
122
|
+
const m = /resp -?\d+[^\0]*/.exec(buffer);
|
|
123
|
+
if (m !== null && buffer.includes("\0")) return m[0];
|
|
124
|
+
await sleep(20);
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
};
|
|
63
128
|
for (const [cmd, line] of COMMANDS) {
|
|
129
|
+
// an instrument the adapter took: does a held note sound? A plugin that dies on the note dies at the remove below
|
|
130
|
+
if (cmd === "remove" && bin === ADAPTER && isInstrument()) {
|
|
131
|
+
const ports = outputPorts((await ask(`audio_ports ${INSTANCE}`)) ?? "");
|
|
132
|
+
sound =
|
|
133
|
+
ports.length === 0
|
|
134
|
+
? { problem: "no output port" }
|
|
135
|
+
: await peakDuring(ports, async () => {
|
|
136
|
+
await ask(NOTE_ON);
|
|
137
|
+
await sleep(NOTE_WINDOW_MS);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
64
140
|
buffer = "";
|
|
65
141
|
sock.write(`${line}\0`);
|
|
66
142
|
const until = Date.now() + STEP_MS;
|
|
@@ -80,7 +156,7 @@ async function main() {
|
|
|
80
156
|
if (resp < 0) break;
|
|
81
157
|
}
|
|
82
158
|
sock.destroy();
|
|
83
|
-
return { installed: true, steps };
|
|
159
|
+
return { installed: true, steps, ...(sound === undefined ? {} : { sound }) };
|
|
84
160
|
}
|
|
85
161
|
|
|
86
162
|
const result = await main();
|