@openmixer/plugin-qualify 0.7.0 → 0.9.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/README.md +55 -3
- package/bin/plugin-qualify.mjs +59 -24
- package/dist/clap-loader.d.ts +25 -1
- package/dist/clap-loader.js +88 -7
- package/dist/clap-loader.js.map +1 -1
- package/dist/clap-qualify.d.ts +3 -2
- package/dist/clap-qualify.js +15 -15
- package/dist/clap-qualify.js.map +1 -1
- package/dist/hosting-suitability.js +1 -1
- package/dist/hosting-suitability.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lv2-loader.d.ts +5 -0
- package/dist/lv2-loader.js +30 -2
- package/dist/lv2-loader.js.map +1 -1
- package/dist/measurement/cli.d.ts +13 -0
- package/dist/measurement/cli.js +68 -2
- package/dist/measurement/cli.js.map +1 -1
- package/dist/pins-file.d.ts +76 -0
- package/dist/pins-file.js +165 -0
- package/dist/pins-file.js.map +1 -0
- package/dist/qualify-cli.d.ts +2 -0
- package/dist/qualify-cli.js +7 -1
- package/dist/qualify-cli.js.map +1 -1
- package/dist/qualify-declarations.d.ts +9 -1
- package/dist/qualify-declarations.js +9 -1
- package/dist/qualify-declarations.js.map +1 -1
- package/dist/qualify-options.js +6 -1
- package/dist/qualify-options.js.map +1 -1
- package/dist/tool-paths.d.ts +35 -1
- package/dist/tool-paths.js +50 -6
- package/dist/tool-paths.js.map +1 -1
- package/package.json +1 -1
- package/src/clap-loader.ts +99 -7
- package/src/clap-qualify.ts +14 -21
- package/src/hosting-suitability.ts +1 -1
- package/src/index.ts +1 -0
- package/src/lv2-loader.ts +28 -2
- package/src/measurement/cli.ts +77 -2
- package/src/pins-file.ts +200 -0
- package/src/qualify-cli.ts +8 -1
- package/src/qualify-declarations.ts +10 -1
- package/src/qualify-options.ts +7 -1
- package/src/tool-paths.ts +68 -5
- package/tools/lv2-measure.mjs +29 -9
package/src/tool-paths.ts
CHANGED
|
@@ -50,11 +50,28 @@ export function lv2MeasureNativePath(): string {
|
|
|
50
50
|
return join(nativeBuildDir(), LV2_MEASURE_TOOL_NAME);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Where the native tools are looked for when no setting names them, in order: the package's own build,
|
|
55
|
+
* then the installed directories. An installed package has no `native/build/`, so its own build first
|
|
56
|
+
* changes nothing there, and in a checkout `make -C native` is never shadowed by an older install.
|
|
57
|
+
*/
|
|
58
|
+
export function nativeToolDirs(): string[] {
|
|
59
|
+
return [nativeBuildDir(), ...NATIVE_TOOL_DIRS];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Where one native tool is looked for when no setting names it, in `nativeToolDirs` order. */
|
|
54
63
|
export function nativeToolCandidates(name: string): string[] {
|
|
55
|
-
return
|
|
64
|
+
return nativeToolDirs().map((dir) => join(dir, name));
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
function settingOf(name: string): string {
|
|
68
|
+
const setting = NATIVE_TOOLS.find((t) => t.name === name)?.env;
|
|
69
|
+
if (setting === undefined) throw new Error(`${name} is not one of NATIVE_TOOLS`);
|
|
70
|
+
return setting;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const isSet = (v: string | undefined): v is string => v !== undefined && v !== "";
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* One of `NATIVE_TOOLS`: its setting when set (and only that), else the first candidate that exists.
|
|
60
77
|
* `path` is absent when none does; `looked` is every place tried, for the refusal to name.
|
|
@@ -64,10 +81,56 @@ export function locateNativeTool(
|
|
|
64
81
|
env: Readonly<Record<string, string | undefined>>,
|
|
65
82
|
exists: (path: string) => boolean,
|
|
66
83
|
): { readonly path?: string; readonly looked: readonly string[]; readonly setting: string } {
|
|
67
|
-
const setting =
|
|
68
|
-
if (setting === undefined) throw new Error(`${name} is not one of NATIVE_TOOLS`);
|
|
84
|
+
const setting = settingOf(name);
|
|
69
85
|
const v = env[setting];
|
|
70
|
-
const looked = v
|
|
86
|
+
const looked = isSet(v) ? [v] : nativeToolCandidates(name);
|
|
71
87
|
const path = looked.find((p) => exists(p));
|
|
72
88
|
return { ...(path === undefined ? {} : { path }), looked, setting };
|
|
73
89
|
}
|
|
90
|
+
|
|
91
|
+
/** One tool of a set `locateNativeTools` located, as `locateNativeTool` reports it. */
|
|
92
|
+
export interface LocatedNativeTool {
|
|
93
|
+
readonly name: string;
|
|
94
|
+
readonly path?: string;
|
|
95
|
+
readonly looked: readonly string[];
|
|
96
|
+
readonly setting: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Tools that run together and share an ABI (a prober or the measurer, and the interposer through
|
|
101
|
+
* `rt-interpose.h`): each set one is its setting; the rest come from ONE directory, the first of
|
|
102
|
+
* `nativeToolDirs` holding any of them. When that directory lacks some of them, `halfPopulated`
|
|
103
|
+
* names it and what it lacks, and the run is refused: pairing a tool from one place with its
|
|
104
|
+
* interposer from another would let a version skew corrupt the verdict without an error.
|
|
105
|
+
*/
|
|
106
|
+
export function locateNativeTools(
|
|
107
|
+
names: readonly string[],
|
|
108
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
109
|
+
exists: (path: string) => boolean,
|
|
110
|
+
): {
|
|
111
|
+
readonly tools: readonly LocatedNativeTool[];
|
|
112
|
+
readonly halfPopulated?: { readonly dir: string; readonly holds: readonly string[]; readonly lacks: readonly string[] };
|
|
113
|
+
} {
|
|
114
|
+
const unset = names.filter((name) => !isSet(env[settingOf(name)]));
|
|
115
|
+
const dir = nativeToolDirs().find((d) => unset.some((name) => exists(join(d, name))));
|
|
116
|
+
const tools = names.map((name): LocatedNativeTool => {
|
|
117
|
+
const setting = settingOf(name);
|
|
118
|
+
const v = env[setting];
|
|
119
|
+
if (isSet(v)) return { name, setting, looked: [v], ...(exists(v) ? { path: v } : {}) };
|
|
120
|
+
const path = dir === undefined ? undefined : join(dir, name);
|
|
121
|
+
return { name, setting, looked: nativeToolCandidates(name), ...(path !== undefined && exists(path) ? { path } : {}) };
|
|
122
|
+
});
|
|
123
|
+
if (dir === undefined) return { tools };
|
|
124
|
+
const lacks = unset.filter((name) => !exists(join(dir, name)));
|
|
125
|
+
if (lacks.length === 0) return { tools };
|
|
126
|
+
return { tools, halfPopulated: { dir, holds: unset.filter((name) => !lacks.includes(name)), lacks } };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The refusal for a half-populated directory: what it holds, what it lacks, and the settings that would decide. */
|
|
130
|
+
export function halfPopulatedRefusal(half: { readonly dir: string; readonly holds: readonly string[]; readonly lacks: readonly string[] }): string {
|
|
131
|
+
const settings = [...half.holds, ...half.lacks].map(settingOf).join(" and ");
|
|
132
|
+
return (
|
|
133
|
+
`${half.dir} holds ${half.holds.join(", ")} but not ${half.lacks.join(", ")}: they share one ABI and are ` +
|
|
134
|
+
`taken from one place, so install or build them together, or set ${settings}`
|
|
135
|
+
);
|
|
136
|
+
}
|
package/tools/lv2-measure.mjs
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* Standalone by construction: it needs `python3` with the `lilv` bindings, the two probers
|
|
9
9
|
* next to this file (`scan.py`, `benchmark.py`), and an LV2 world to look at; the hosting
|
|
10
10
|
* dimensions need the package's native measurer and interposer (`lv2_measure`,
|
|
11
|
-
* `rt_interpose.so`), each its setting (`OMX_LV2_MEASURE`, `OMX_RT_INTERPOSE`), else
|
|
12
|
-
*
|
|
11
|
+
* `rt_interpose.so`), each its setting (`OMX_LV2_MEASURE`, `OMX_RT_INTERPOSE`), else
|
|
12
|
+
* `native/build` (`make -C native`), else where the package installs it. It does not
|
|
13
13
|
* need the OpenMixer engine, its server, PipeWire, JACK, an audio interface, or root. Nothing
|
|
14
14
|
* it writes is openmixer-shaped — the output is the interchange format described in
|
|
15
15
|
* `src/measurement/format.ts`, which any LV2 host can read.
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
urisFromScan,
|
|
61
61
|
} from "../dist/measurement/index.js";
|
|
62
62
|
import { LV2_HOSTING_TIMEOUT_S, LV2_MEASURE_TOOL_NAME, RT_INTERPOSE_NAME } from "../dist/qualify-declarations.js";
|
|
63
|
-
import {
|
|
63
|
+
import { halfPopulatedRefusal, locateNativeTools } from "../dist/tool-paths.js";
|
|
64
64
|
|
|
65
65
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
66
66
|
|
|
@@ -85,6 +85,22 @@ const HELP = `${TOOL_NAME} ${TOOL_VERSION} — measure LV2 plugin latency and CP
|
|
|
85
85
|
--dry-run Print the plan and probe nothing.
|
|
86
86
|
--progress-json Emit machine-readable progress on stderr, one '@progress {...}' per line.
|
|
87
87
|
--help This text.
|
|
88
|
+
|
|
89
|
+
The hosting pass's host features are omx-clap-core's LV2 feature provider; each of its options is
|
|
90
|
+
a flag, passed to every run of lv2_measure and named in each plugin's record (featureOptions):
|
|
91
|
+
|
|
92
|
+
--features LIST Kinds offered: map,unmap,options,bounded,worker,default-state,log
|
|
93
|
+
(default: all but default-state; empty: none).
|
|
94
|
+
--strict Refuse a plugin requiring a feature not offered, naming it (refusedFeature).
|
|
95
|
+
--no-strict Hand it to its own instantiate without it (default).
|
|
96
|
+
--block-declares LIST Buf-size declarations when bounded is offered: bounded,fixed,pow2
|
|
97
|
+
(default: bounded; empty: none).
|
|
98
|
+
--block-min N bufsz:minBlockLength (default: 1; 0: the run's block).
|
|
99
|
+
--block-max N bufsz:maxBlockLength (default: 0, the run's block).
|
|
100
|
+
--block-nominal N bufsz:nominalBlockLength (default: 0, the maximum).
|
|
101
|
+
--log MODE log:log lines: silent, route (to stderr) or capture (into the record)
|
|
102
|
+
(default: silent).
|
|
103
|
+
--log-capture-bytes N The capture buffer, its NUL included (default: 4096).
|
|
88
104
|
`;
|
|
89
105
|
|
|
90
106
|
function fail(message, code = 2) {
|
|
@@ -96,16 +112,20 @@ function readJson(path) {
|
|
|
96
112
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* The native measurer and its interposer, located together (`locateNativeTools`): absent, or half there
|
|
117
|
+
* in the one place they come from, the run is refused naming where it looked.
|
|
118
|
+
*/
|
|
100
119
|
function hostingTools() {
|
|
101
|
-
const
|
|
102
|
-
|
|
120
|
+
const { tools, halfPopulated } = locateNativeTools([LV2_MEASURE_TOOL_NAME, RT_INTERPOSE_NAME], process.env, existsSync);
|
|
121
|
+
if (halfPopulated !== undefined) fail(`no measurer and interposer pair: ${halfPopulatedRefusal(halfPopulated)}`);
|
|
122
|
+
const [tool, interposer] = tools.map((found) => {
|
|
103
123
|
if (found.path === undefined) {
|
|
104
|
-
fail(`no ${name} at ${found.looked.join(", ")}: install the plugin-qualify package, build it with make -C native, or set ${found.setting}`);
|
|
124
|
+
fail(`no ${found.name} at ${found.looked.join(", ")}: install the plugin-qualify package, build it with make -C native, or set ${found.setting}`);
|
|
105
125
|
}
|
|
106
126
|
return found.path;
|
|
107
|
-
};
|
|
108
|
-
return { tool
|
|
127
|
+
});
|
|
128
|
+
return { tool, interposer };
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
/** The hosting pass: the measurer once per plugin, each in its own process, its lines written as JSONL. */
|