@openmixer/omx-contract 2.10.1 → 2.11.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 +1 -1
- package/data/kernels/drive.json +11 -5
- package/dist/ts/omx-contract.d.ts +12 -2
- package/dist/ts/omx-contract.js +14 -4
- package/include/omxcontract/omx_contract_limits.h +14 -4
- package/lib/data.mjs +2 -1
- package/lib/semver.mjs +13 -4
- package/lib/validate.mjs +11 -0
- package/package.json +1 -1
- package/render/c.mjs +20 -1
- package/render/doors.mjs +1 -1
- package/render/ts.mjs +21 -3
- package/schema/omx-contract.schema.json +10 -2
- package/share/omx-contract/omx-contract.json +11 -5
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ pkg-config --cflags omx-contract
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Pin one version and build against its renders instead of keeping a copy: the installed package at exactly
|
|
52
|
-
that version (`pkg-config --exact-version=2.
|
|
52
|
+
that version (`pkg-config --exact-version=2.11.0 omx-contract`), else the npm tarball, unpacked, and
|
|
53
53
|
`omx-contract render --target c --out build/omx-contract/include`. A kernel's controls and readouts, in their
|
|
54
54
|
order, are in `share/omx-contract/omx-contract.json`; how a declaration is written is in
|
|
55
55
|
[docs/kernel-files.md](docs/kernel-files.md), and what a readout is in [docs/readouts.md](docs/readouts.md).
|
package/data/kernels/drive.json
CHANGED
|
@@ -149,17 +149,23 @@
|
|
|
149
149
|
{
|
|
150
150
|
"name": "hfRolloff",
|
|
151
151
|
"kind": "choice",
|
|
152
|
-
"doc": "The drive's post-curve HIGH-FREQUENCY ROLL-OFF
|
|
152
|
+
"doc": "The drive's post-curve HIGH-FREQUENCY ROLL-OFF: `off` (the section does not run: `omx_drive.h`'s `struct omx_drive.hf_on` is 0), `12000` or `16000`, each carrying its corner in Hz as its value and `off` none (operator ruling, 2026-10-13: the choice is an enumeration and the frequency is data of each member, so no 0 Hz doubles as a sentinel). The ids are the console's drive-row wire vocabulary (`DriveState.hfRolloff`, what the drive row's OPTIONS publishes and a session stores), so the console re-exports this set; a kernel reads the corner from the id's value. It comes up off, as omx-dsp's drive face does.",
|
|
153
|
+
"unit": "Hz",
|
|
153
154
|
"ids": [
|
|
154
|
-
|
|
155
|
-
12000,
|
|
156
|
-
16000
|
|
155
|
+
"off",
|
|
156
|
+
"12000",
|
|
157
|
+
"16000"
|
|
157
158
|
],
|
|
158
|
-
"default":
|
|
159
|
+
"default": "off",
|
|
159
160
|
"labels": [
|
|
160
161
|
"Off",
|
|
161
162
|
"12 kHz",
|
|
162
163
|
"16 kHz"
|
|
164
|
+
],
|
|
165
|
+
"values": [
|
|
166
|
+
null,
|
|
167
|
+
12000,
|
|
168
|
+
16000
|
|
163
169
|
]
|
|
164
170
|
},
|
|
165
171
|
{
|
|
@@ -543,10 +543,10 @@ export declare const DRIVE_AUTO_GAINS: readonly ["off", "on"];
|
|
|
543
543
|
export declare const DRIVE_STEREO_LINKS: readonly ["off", "on"];
|
|
544
544
|
|
|
545
545
|
/**
|
|
546
|
-
* The drive's post-curve HIGH-FREQUENCY ROLL-OFF
|
|
546
|
+
* The drive's post-curve HIGH-FREQUENCY ROLL-OFF: `off` (the section does not run: `omx_drive.h`'s `struct omx_drive.hf_on` is 0), `12000` or `16000`, each carrying its corner in Hz as its value and `off` none (operator ruling, 2026-10-13: the choice is an enumeration and the frequency is data of each member, so no 0 Hz doubles as a sentinel). The ids are the console's drive-row wire vocabulary (`DriveState.hfRolloff`, what the drive row's OPTIONS publishes and a session stores), so the console re-exports this set; a kernel reads the corner from the id's value. It comes up off, as omx-dsp's drive face does.
|
|
547
547
|
* @see data/kernels/drive.json
|
|
548
548
|
*/
|
|
549
|
-
export declare const DRIVE_HF_ROLLOFFS: readonly [
|
|
549
|
+
export declare const DRIVE_HF_ROLLOFFS: readonly ["off", "12000", "16000"];
|
|
550
550
|
|
|
551
551
|
/**
|
|
552
552
|
* The drive's OVERSAMPLING factor: the shaper runs inside the console's one oversampler at 1x, 2x or 4x, and the id is the factor (`omx_drive.h`'s `omx_drive_factor_of` member set, `struct omx_drive.os_factor`). A field of the console's drive row (`DriveState.oversample`, the console's DRIVE_OVERSAMPLE_FACTORS; the set is named for its control), 4 at come-up there and in omx-dsp's drive face (OMX_DRIVE_INSTANCE_FACTOR, the oversampler's largest).
|
|
@@ -2132,6 +2132,16 @@ export declare const ORACLE_FLOOR_RATES: readonly [44100, 48000, 96000, 192000];
|
|
|
2132
2132
|
*/
|
|
2133
2133
|
export declare const RME_RATES: readonly [32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000];
|
|
2134
2134
|
|
|
2135
|
+
/**
|
|
2136
|
+
* The value each id of DRIVE_HF_ROLLOFFS carries, in "Hz"; null for an id that carries none.
|
|
2137
|
+
* @see data/kernels/drive.json
|
|
2138
|
+
*/
|
|
2139
|
+
export declare const DRIVE_HF_ROLLOFFS_VALUES: {
|
|
2140
|
+
readonly "12000": 12000;
|
|
2141
|
+
readonly "16000": 16000;
|
|
2142
|
+
readonly off: null;
|
|
2143
|
+
};
|
|
2144
|
+
|
|
2135
2145
|
/**
|
|
2136
2146
|
* Every kernel's readouts, the meters a host draws, each kernel's in its declared order: the position is the
|
|
2137
2147
|
* readout's id. Every reference is resolved.
|
package/dist/ts/omx-contract.js
CHANGED
|
@@ -578,13 +578,13 @@ export const DRIVE_STEREO_LINKS = [
|
|
|
578
578
|
];
|
|
579
579
|
|
|
580
580
|
/**
|
|
581
|
-
* The drive's post-curve HIGH-FREQUENCY ROLL-OFF
|
|
581
|
+
* The drive's post-curve HIGH-FREQUENCY ROLL-OFF: `off` (the section does not run: `omx_drive.h`'s `struct omx_drive.hf_on` is 0), `12000` or `16000`, each carrying its corner in Hz as its value and `off` none (operator ruling, 2026-10-13: the choice is an enumeration and the frequency is data of each member, so no 0 Hz doubles as a sentinel). The ids are the console's drive-row wire vocabulary (`DriveState.hfRolloff`, what the drive row's OPTIONS publishes and a session stores), so the console re-exports this set; a kernel reads the corner from the id's value. It comes up off, as omx-dsp's drive face does.
|
|
582
582
|
* @see data/kernels/drive.json
|
|
583
583
|
*/
|
|
584
584
|
export const DRIVE_HF_ROLLOFFS = [
|
|
585
|
-
|
|
586
|
-
12000,
|
|
587
|
-
16000
|
|
585
|
+
"off",
|
|
586
|
+
"12000",
|
|
587
|
+
"16000"
|
|
588
588
|
];
|
|
589
589
|
|
|
590
590
|
/**
|
|
@@ -2351,6 +2351,16 @@ export const RME_RATES = [
|
|
|
2351
2351
|
192000
|
|
2352
2352
|
];
|
|
2353
2353
|
|
|
2354
|
+
/**
|
|
2355
|
+
* The value each id of DRIVE_HF_ROLLOFFS carries, in "Hz"; null for an id that carries none.
|
|
2356
|
+
* @see data/kernels/drive.json
|
|
2357
|
+
*/
|
|
2358
|
+
export const DRIVE_HF_ROLLOFFS_VALUES = {
|
|
2359
|
+
"12000": 12000,
|
|
2360
|
+
"16000": 16000,
|
|
2361
|
+
"off": null
|
|
2362
|
+
};
|
|
2363
|
+
|
|
2354
2364
|
/**
|
|
2355
2365
|
* Every kernel's readouts, the meters a host draws, each kernel's in its declared order: the position is the
|
|
2356
2366
|
* readout's id. Every reference is resolved.
|
|
@@ -690,12 +690,22 @@ enum omx_drive_curves {
|
|
|
690
690
|
};
|
|
691
691
|
#define OMX_DRIVE_CURVES_COUNT 4u
|
|
692
692
|
enum omx_drive_hf_rolloffs {
|
|
693
|
-
|
|
694
|
-
OMX_DRIVE_HF_ROLLOFFS_12000 =
|
|
695
|
-
OMX_DRIVE_HF_ROLLOFFS_16000 =
|
|
693
|
+
OMX_DRIVE_HF_ROLLOFFS_OFF = 0,
|
|
694
|
+
OMX_DRIVE_HF_ROLLOFFS_12000 = 1,
|
|
695
|
+
OMX_DRIVE_HF_ROLLOFFS_16000 = 2,
|
|
696
696
|
};
|
|
697
697
|
#define OMX_DRIVE_HF_ROLLOFFS_COUNT 3u
|
|
698
|
-
#define OMX_DRIVE_HF_ROLLOFFS_DEFAULT
|
|
698
|
+
#define OMX_DRIVE_HF_ROLLOFFS_DEFAULT OMX_DRIVE_HF_ROLLOFFS_OFF
|
|
699
|
+
#define OMX_DRIVE_HF_ROLLOFFS_UNIT "Hz"
|
|
700
|
+
#define OMX_DRIVE_HF_ROLLOFFS_12000_VALUE 12000.0f
|
|
701
|
+
#define OMX_DRIVE_HF_ROLLOFFS_16000_VALUE 16000.0f
|
|
702
|
+
static inline int omx_drive_hf_rolloffs_value(int id, float *out) {
|
|
703
|
+
switch (id) {
|
|
704
|
+
case OMX_DRIVE_HF_ROLLOFFS_12000: *out = OMX_DRIVE_HF_ROLLOFFS_12000_VALUE; return 1;
|
|
705
|
+
case OMX_DRIVE_HF_ROLLOFFS_16000: *out = OMX_DRIVE_HF_ROLLOFFS_16000_VALUE; return 1;
|
|
706
|
+
default: return 0;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
699
709
|
enum omx_drive_oversamples {
|
|
700
710
|
OMX_DRIVE_OVERSAMPLES_1 = 1,
|
|
701
711
|
OMX_DRIVE_OVERSAMPLES_2 = 2,
|
package/lib/data.mjs
CHANGED
|
@@ -262,7 +262,7 @@ export function lagrangeReadL1(order) {
|
|
|
262
262
|
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
263
263
|
|
|
264
264
|
/**
|
|
265
|
-
* Resolve every item: `{ name, rel, kind, doc, why?, unit?, c?, shape?, default?, labels?, value }` (default and
|
|
265
|
+
* Resolve every item: `{ name, rel, kind, doc, why?, unit?, c?, shape?, default?, labels?, values?, value }` (default, labels and values: a set's), where `value` has
|
|
266
266
|
* every `{ref}` and `{derive}` evaluated. The returned map also carries `kernels`, every kernel's ordered controls
|
|
267
267
|
* (kernelControls) and, for a kernel that declares them, its ordered `readouts` `{ name, doc, unit, min, max, scale,
|
|
268
268
|
* rateHz }` with every reference resolved, which the json render publishes beside the items.
|
|
@@ -305,6 +305,7 @@ export function resolveData(data) {
|
|
|
305
305
|
if (item.kind === 'set') {
|
|
306
306
|
if (item.default !== undefined) r.default = setDefault(item, (v) => val(v, name), name);
|
|
307
307
|
if (item.labels !== undefined) r.labels = item.labels;
|
|
308
|
+
if (item.values !== undefined) r.values = val(item.values, name);
|
|
308
309
|
}
|
|
309
310
|
if (item.kind === 'travels') r.shape = item.fields ? 'table' : 'travel';
|
|
310
311
|
r.value = val(rawValue(item), name);
|
package/lib/semver.mjs
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* major a field or alias removed; an item removed (MINOR in the development phase, below); a
|
|
8
8
|
* unit or kind changed; a travel narrowed; a constant a kernel computes with changed (a
|
|
9
9
|
* scalar, sheet leaf or derived value that is not a travel bound or default; a list member
|
|
10
|
-
* or set id changed or removed; a set's default added, removed
|
|
10
|
+
* or set id changed or removed; a set's default added, removed; a value an id carries changed
|
|
11
|
+
* or removed)
|
|
11
12
|
* minor an item, field, sheet leaf, list member or set id added (appended); a travel widened;
|
|
12
|
-
* a default moved inside its travel; a step changed
|
|
13
|
+
* a default moved inside its travel; a step changed; a set's values added
|
|
13
14
|
* patch doc and why text
|
|
14
15
|
*
|
|
15
16
|
* A kernel's readouts (docs/readouts.md): one removed, renamed or moved, or its unit changed, is major (a removal is
|
|
@@ -94,7 +95,8 @@ export function classify(prev, next, { developmentPhase = DEVELOPMENT_PHASE } =
|
|
|
94
95
|
continue;
|
|
95
96
|
}
|
|
96
97
|
if (a.kind !== b.kind || (a.shape ?? '') !== (b.shape ?? '')) { say('major', `kind ${a.kind} -> ${b.kind}`); continue; }
|
|
97
|
-
|
|
98
|
+
// a set that gains values gains their unit with them (minor, below); any other unit change is major
|
|
99
|
+
if ((a.unit ?? '') !== (b.unit ?? '') && !(a.kind === 'set' && a.values === undefined)) say('major', `unit ${JSON.stringify(a.unit)} -> ${JSON.stringify(b.unit)}`);
|
|
98
100
|
if (a.doc !== b.doc || (a.why ?? '') !== (b.why ?? '')) say('patch', 'doc');
|
|
99
101
|
if (!same(a.c?.alias, b.c?.alias)) say(a.c?.alias ? 'major' : 'minor', a.c?.alias ? 'c.alias removed or renamed' : 'c.alias added');
|
|
100
102
|
if (!same(a.c?.render, b.c?.render)) say(b.c?.render === false ? 'major' : 'minor', 'C render of the list changed');
|
|
@@ -123,11 +125,18 @@ export function classify(prev, next, { developmentPhase = DEVELOPMENT_PHASE } =
|
|
|
123
125
|
case 'set':
|
|
124
126
|
a.value.forEach((v, i) => {
|
|
125
127
|
if (i >= b.value.length) say('major', `${name}[${i}] removed`);
|
|
126
|
-
else if (b.value[i] !== v) say('major', `${name}[${i}]: ${v} -> ${b.value[i]}`);
|
|
128
|
+
else if (b.value[i] !== v) say('major', `${name}[${i}]: ${JSON.stringify(v)} -> ${JSON.stringify(b.value[i])}`);
|
|
127
129
|
});
|
|
128
130
|
if (b.value.length > a.value.length) say('minor', `${name}: ${b.value.length - a.value.length} id(s) appended`);
|
|
129
131
|
if (a.default !== b.default) say(a.default === undefined || b.default === undefined ? 'major' : 'minor', `${name}: default ${a.default} -> ${b.default}`);
|
|
130
132
|
if (!same(a.labels, b.labels)) say('patch', `${name}: labels`);
|
|
133
|
+
if (a.values === undefined && b.values !== undefined) say('minor', `${name}: values added`);
|
|
134
|
+
else if (a.values !== undefined && b.values === undefined) say('major', `${name}: values removed`);
|
|
135
|
+
else if (a.values !== undefined) {
|
|
136
|
+
a.values.forEach((v, i) => {
|
|
137
|
+
if (i < b.values.length && b.values[i] !== v) say('major', `${name}: the value of ${a.value[i]}: ${v} -> ${b.values[i]} (a constant a kernel computes with)`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
131
140
|
break;
|
|
132
141
|
default:
|
|
133
142
|
break;
|
package/lib/validate.mjs
CHANGED
|
@@ -112,6 +112,14 @@ function semanticRules(resolved, say) {
|
|
|
112
112
|
if (r.kind === 'set') {
|
|
113
113
|
if (r.default !== undefined && !r.value.includes(r.default)) say(r.rel, r.name, 'set-default', `default ${JSON.stringify(r.default)} is not one of ${JSON.stringify(r.value)}`);
|
|
114
114
|
if (r.labels !== undefined && r.labels.length !== r.value.length) say(r.rel, r.name, 'set-labels', `${r.labels.length} labels for ${r.value.length} ids`);
|
|
115
|
+
if (r.values !== undefined) {
|
|
116
|
+
if (r.values.length !== r.value.length) say(r.rel, r.name, 'set-values', `${r.values.length} values for ${r.value.length} ids`);
|
|
117
|
+
if (r.value.some((id) => typeof id !== 'string')) say(r.rel, r.name, 'set-values', 'a set of numeric ids carries no values: the id is its own value');
|
|
118
|
+
r.values.forEach((v, i) => {
|
|
119
|
+
if (v !== null && (typeof v !== 'number' || !Number.isFinite(v))) say(r.rel, r.name, 'set-values', `the value of ${JSON.stringify(r.value[i])} resolves to ${JSON.stringify(v)}`);
|
|
120
|
+
});
|
|
121
|
+
if (r.values.every((v) => v === null)) say(r.rel, r.name, 'set-values', 'no id carries a value');
|
|
122
|
+
}
|
|
115
123
|
}
|
|
116
124
|
const travels = r.kind === 'travels' ? (r.shape === 'table' ? Object.entries(r.value) : [['', r.value]]) : [];
|
|
117
125
|
for (const [field, t] of travels) {
|
|
@@ -130,6 +138,9 @@ function semanticRules(resolved, say) {
|
|
|
130
138
|
if (!(val('PARAM_SCALES') ?? []).includes(ro.scale)) say(rel, at, 'scale', `${at}: scale ${JSON.stringify(ro.scale)} is not an id of PARAM_SCALES`);
|
|
131
139
|
}
|
|
132
140
|
}
|
|
141
|
+
for (const r of resolved.values()) {
|
|
142
|
+
if (r.kind === 'set' && r.values && resolved.has(`${r.name}_VALUES`)) say(r.rel, `${r.name}_VALUES`, 'set-values', `${r.name}_VALUES is the ts render's export of ${r.name}'s values: no item may take the name`);
|
|
143
|
+
}
|
|
133
144
|
if (resolved.has('READOUTS') && Object.values(resolved.kernels ?? {}).some((k) => k.readouts)) {
|
|
134
145
|
say(resolved.get('READOUTS').rel, 'READOUTS', 'readouts-name', 'READOUTS is the ts render\'s export of the readouts: no item may take the name');
|
|
135
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmixer/omx-contract",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "The FreeMixer declarations as data: a JSON Schema, the data, and one CLI that renders them into C, TypeScript and JSON",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
package/render/c.mjs
CHANGED
|
@@ -43,8 +43,26 @@ export const setEnumerators = (set) => set.ids.map((id) => `OMX_${set.name}_${co
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Every set as a C enum: the id's position is its value for named ids, the number itself for numeric
|
|
46
|
-
* ids; `OMX_<SET>_COUNT`, and `OMX_<SET>_DEFAULT` when the set names a default.
|
|
46
|
+
* ids; `OMX_<SET>_COUNT`, and `OMX_<SET>_DEFAULT` when the set names a default. A set whose ids carry values adds
|
|
47
|
+
* `OMX_<SET>_UNIT`, `OMX_<SET>_<ID>_VALUE` for each id that carries one, and `omx_<set>_value(id, &out)`, which writes
|
|
48
|
+
* the id's value and returns 1, or returns 0 for an id that carries none: a kernel reads the number from the id
|
|
49
|
+
* (the drive's roll-off corner), never a sentinel.
|
|
47
50
|
*/
|
|
51
|
+
function genSetValues(set, names) {
|
|
52
|
+
const fn = `omx_${set.name.toLowerCase()}_value`;
|
|
53
|
+
const carried = set.ids.map((id, i) => [names[i], set.values[i]]).filter(([, v]) => v !== null);
|
|
54
|
+
return [
|
|
55
|
+
`#define OMX_${set.name}_UNIT ${cStringLiteral(set.unit)}`,
|
|
56
|
+
...carried.map(([n, v]) => `#define ${n}_VALUE ${floatLiteral(v)}`),
|
|
57
|
+
`static inline int ${fn}(int id, float *out) {`,
|
|
58
|
+
' switch (id) {',
|
|
59
|
+
...carried.map(([n]) => ` case ${n}: *out = ${n}_VALUE; return 1;`),
|
|
60
|
+
' default: return 0;',
|
|
61
|
+
' }',
|
|
62
|
+
'}',
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
48
66
|
function genDeclaredSetsEnums(sets) {
|
|
49
67
|
return sets.map((set) => {
|
|
50
68
|
const names = setEnumerators(set);
|
|
@@ -52,6 +70,7 @@ function genDeclaredSetsEnums(sets) {
|
|
|
52
70
|
const members = set.ids.map((id, i) => ` ${names[i]} = ${numeric ? id : i},`);
|
|
53
71
|
const lines = [`enum omx_${set.name.toLowerCase()} {`, ...members, '};', `#define OMX_${set.name}_COUNT ${set.ids.length}u`];
|
|
54
72
|
if (set.default !== undefined) lines.push(`#define OMX_${set.name}_DEFAULT ${names[set.ids.indexOf(set.default)]}`);
|
|
73
|
+
if (set.values) lines.push(...genSetValues(set, names));
|
|
55
74
|
return lines.join('\n');
|
|
56
75
|
}).join('\n');
|
|
57
76
|
}
|
package/render/doors.mjs
CHANGED
|
@@ -52,7 +52,7 @@ export function doors(resolved) {
|
|
|
52
52
|
.map((r) => ({ name: r.name, values: r.value }))
|
|
53
53
|
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
54
54
|
const DECLARED_SET_ITEMS = items.filter((r) => r.kind === 'set')
|
|
55
|
-
.map((r) => ({ name: r.name, ids: r.value, default: r.default }))
|
|
55
|
+
.map((r) => ({ name: r.name, ids: r.value, default: r.default, ...(r.values ? { values: r.values, unit: r.unit } : {}) }))
|
|
56
56
|
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
57
57
|
const ALIASES = new Map();
|
|
58
58
|
for (const r of items) {
|
package/render/ts.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export function tsValue(r) {
|
|
|
14
14
|
|
|
15
15
|
/** The literal type of a JSON value, every member readonly. */
|
|
16
16
|
export function tsType(v, indent = '') {
|
|
17
|
-
if (typeof v === 'number' || typeof v === 'boolean') return String(v);
|
|
17
|
+
if (v === null || typeof v === 'number' || typeof v === 'boolean') return String(v);
|
|
18
18
|
if (typeof v === 'string') return JSON.stringify(v);
|
|
19
19
|
const inner = `${indent} `;
|
|
20
20
|
if (Array.isArray(v)) return v.length ? `readonly [${v.map((x) => tsType(x, indent)).join(', ')}]` : 'readonly []';
|
|
@@ -83,12 +83,30 @@ function readoutsExport(resolved) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* The value each id of a set carries, `<SET>_VALUES`: `{ <id>: value | null }`, for every set that declares values (the
|
|
88
|
+
* drive's roll-off corners), so a reader takes the number from the id, never from its spelling. Read it by id: an
|
|
89
|
+
* object keeps no order for an id such as 12000.
|
|
90
|
+
*/
|
|
91
|
+
function setValuesExports(resolved) {
|
|
92
|
+
const sets = [...resolved.values()].filter((r) => r.kind === 'set' && r.values);
|
|
93
|
+
const one = (r, decl) => {
|
|
94
|
+
const value = Object.fromEntries(r.value.map((id, i) => [id, r.values[i]]));
|
|
95
|
+
const doc = `/**\n * The value each id of ${r.name} carries, in ${JSON.stringify(r.unit)}; null for an id that carries none.\n * @see ${r.rel}\n */`;
|
|
96
|
+
return decl
|
|
97
|
+
? `\n${doc}\nexport declare const ${r.name}_VALUES: ${tsType(value)};\n`
|
|
98
|
+
: `\n${doc}\nexport const ${r.name}_VALUES = ${JSON.stringify(value, null, 2)};\n`;
|
|
99
|
+
};
|
|
100
|
+
return { js: sets.map((r) => one(r, false)).join(''), dts: sets.map((r) => one(r, true)).join('') };
|
|
101
|
+
}
|
|
102
|
+
|
|
86
103
|
/** Every file of the `ts` target: `[{ path, text }]`. */
|
|
87
104
|
export function renderTs(resolved) {
|
|
88
105
|
const items = [...resolved.values()];
|
|
89
106
|
const rule = eqRule(resolved);
|
|
90
107
|
const readouts = readoutsExport(resolved);
|
|
91
|
-
const
|
|
92
|
-
const
|
|
108
|
+
const values = setValuesExports(resolved);
|
|
109
|
+
const js = BANNER + items.map((r) => `\n${docComment(r)}\nexport const ${r.name} = ${JSON.stringify(tsValue(r), null, 2)};\n`).join('') + values.js + readouts.js + rule.js;
|
|
110
|
+
const dts = BANNER + items.map((r) => `\n${docComment(r)}\nexport declare const ${r.name}: ${tsType(tsValue(r))};\n`).join('') + values.dts + readouts.dts + rule.dts;
|
|
93
111
|
return [{ path: 'omx-contract.js', text: js }, { path: 'omx-contract.d.ts', text: dts }];
|
|
94
112
|
}
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
},
|
|
182
182
|
"set": {
|
|
183
183
|
"type": "object",
|
|
184
|
-
"description": "Ordered ids with an optional default and
|
|
184
|
+
"description": "Ordered ids with an optional default, labels and a value per id. The ids and their order are the contract: all strings of letters and digits (the id is the name, its position the value; an id may start with a digit, as 4x does) or all non-negative integers (the id is the value). Labels are face text and optional. The default may be a reference to the scalar that states it: a boolean names the second id of a two-id switch when true, the first when false.",
|
|
185
185
|
"properties": {
|
|
186
186
|
"kind": { "const": "set" },
|
|
187
187
|
"since": { "$ref": "#/$defs/since" },
|
|
@@ -194,9 +194,17 @@
|
|
|
194
194
|
]
|
|
195
195
|
},
|
|
196
196
|
"default": { "oneOf": [{ "type": "string", "pattern": "^[A-Za-z0-9]+$" }, { "type": "integer", "minimum": 0 }, { "$ref": "#/$defs/ref" }] },
|
|
197
|
-
"labels": { "type": "array", "items": { "$ref": "#/$defs/text" }, "minItems": 1 }
|
|
197
|
+
"labels": { "type": "array", "items": { "$ref": "#/$defs/text" }, "minItems": 1 },
|
|
198
|
+
"unit": { "$ref": "#/$defs/unit" },
|
|
199
|
+
"values": {
|
|
200
|
+
"description": "The value each id carries, in `unit`, one per id in id order; null for an id that carries none (the drive's `off` roll-off has no corner). Only a set of named ids carries values: a numeric id is its own value.",
|
|
201
|
+
"type": "array",
|
|
202
|
+
"items": { "oneOf": [{ "$ref": "#/$defs/number" }, { "type": "null" }] },
|
|
203
|
+
"minItems": 1
|
|
204
|
+
}
|
|
198
205
|
},
|
|
199
206
|
"required": ["kind", "doc", "ids"],
|
|
207
|
+
"dependentRequired": { "values": ["unit"], "unit": ["values"] },
|
|
200
208
|
"additionalProperties": false
|
|
201
209
|
},
|
|
202
210
|
"item": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmixer/omx-contract",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"items": {
|
|
5
5
|
"PAN_RANGE": {
|
|
6
6
|
"rel": "data/kernels/balance.json",
|
|
@@ -686,17 +686,23 @@
|
|
|
686
686
|
"DRIVE_HF_ROLLOFFS": {
|
|
687
687
|
"rel": "data/kernels/drive.json",
|
|
688
688
|
"kind": "set",
|
|
689
|
-
"doc": "The drive's post-curve HIGH-FREQUENCY ROLL-OFF
|
|
690
|
-
"
|
|
689
|
+
"doc": "The drive's post-curve HIGH-FREQUENCY ROLL-OFF: `off` (the section does not run: `omx_drive.h`'s `struct omx_drive.hf_on` is 0), `12000` or `16000`, each carrying its corner in Hz as its value and `off` none (operator ruling, 2026-10-13: the choice is an enumeration and the frequency is data of each member, so no 0 Hz doubles as a sentinel). The ids are the console's drive-row wire vocabulary (`DriveState.hfRolloff`, what the drive row's OPTIONS publishes and a session stores), so the console re-exports this set; a kernel reads the corner from the id's value. It comes up off, as omx-dsp's drive face does.",
|
|
690
|
+
"unit": "Hz",
|
|
691
|
+
"default": "off",
|
|
691
692
|
"labels": [
|
|
692
693
|
"Off",
|
|
693
694
|
"12 kHz",
|
|
694
695
|
"16 kHz"
|
|
695
696
|
],
|
|
696
|
-
"
|
|
697
|
-
|
|
697
|
+
"values": [
|
|
698
|
+
null,
|
|
698
699
|
12000,
|
|
699
700
|
16000
|
|
701
|
+
],
|
|
702
|
+
"value": [
|
|
703
|
+
"off",
|
|
704
|
+
"12000",
|
|
705
|
+
"16000"
|
|
700
706
|
]
|
|
701
707
|
},
|
|
702
708
|
"DRIVE_OVERSAMPLES": {
|