@openmixer/omx-contract 1.3.0 → 2.1.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 +68 -2
- package/bin/omx-contract.mjs +14 -3
- package/data/kernels/balance.json +18 -14
- package/data/kernels/band_dyn.json +132 -0
- package/data/kernels/chorus.json +87 -78
- package/data/kernels/comp.json +67 -29
- package/data/kernels/deesser.json +114 -94
- package/data/kernels/delay.json +67 -57
- package/data/kernels/drive.json +118 -107
- package/data/kernels/eq.json +282 -267
- package/data/kernels/fbs.json +144 -142
- package/data/kernels/flanger.json +74 -66
- package/data/kernels/gate.json +76 -29
- package/data/kernels/geq.json +32 -27
- package/data/kernels/hrp.json +62 -56
- package/data/kernels/limiter.json +26 -9
- package/data/kernels/mixmatrix.json +7 -5
- package/data/kernels/phaser.json +110 -106
- package/data/kernels/pitch.json +35 -17
- package/data/kernels/reverb.json +176 -159
- package/data/kernels/rotary.json +189 -186
- package/data/kernels/rta.json +13 -11
- package/data/kernels/transient.json +64 -36
- package/data/kernels/tremolo.json +59 -57
- package/dist/ts/omx-contract.d.ts +157 -67
- package/dist/ts/omx-contract.js +211 -118
- package/include/omxcontract/omx_contract_limits.h +35 -0
- package/lib/data.mjs +134 -3
- package/lib/fmt.mjs +22 -2
- package/lib/validate.mjs +43 -2
- package/package.json +1 -1
- package/render/json.mjs +6 -2
- package/share/omx-contract/omx-contract.json +865 -128
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ omx-dsp needs, 1.1.0 adds the flanger, de-esser, phaser, rotary, tremolo and gra
|
|
|
12
12
|
RTA constants, the EQ band budget, the choice enums (as the `set` kind), the EQ band counts and the
|
|
13
13
|
one rule that gives a fresh EQ its bands, the measurement rate sets and the bus input cap. What is
|
|
14
14
|
declared here is declared nowhere else: the EQ band budget that 1.2.0 left in openmixer is here too.
|
|
15
|
+
2.0.0 changes how a kernel file is written, not what it declares: every render is the same.
|
|
16
|
+
2.1.0 adds each kernel's ordered controls to the JSON render, and a control's `rearms` flag.
|
|
15
17
|
|
|
16
18
|
```
|
|
17
19
|
data/ the declaration, one file per group, edited by hand
|
|
@@ -30,6 +32,70 @@ tools/seed/ the one-time seed from openmixer's built core (prov
|
|
|
30
32
|
packaging/ debian/ omx-contract-devel (RPM), libomx-contract-dev (deb)
|
|
31
33
|
```
|
|
32
34
|
|
|
35
|
+
## The kernel file
|
|
36
|
+
|
|
37
|
+
A kernel file, `data/kernels/<kernel>.json`, holds four sections and nothing else. A sketch, values abridged
|
|
38
|
+
(the tremolo's controls, a gate table, a constant):
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"$schema": "../../schema/omx-contract.schema.json",
|
|
43
|
+
"controls": [
|
|
44
|
+
{ "name": "rateHz", "kind": "travel", "global": "TREMOLO_RATE_RANGE", "doc": "…", "travel": { "min": 0.1, "max": 20, "unit": "Hz", "default": 5 } },
|
|
45
|
+
{ "name": "depth", "kind": "travel", "doc": "…", "travel": { "min": 0, "max": 1, "unit": "", "default": 0.5 } },
|
|
46
|
+
{ "name": "mode", "kind": "choice", "doc": "…", "ids": ["tremolo", "pan"], "labels": ["Tremolo", "Pan"] },
|
|
47
|
+
{ "name": "thresholdDb", "kind": "travel", "table": "GATE_LIMITS", "travel": { "min": -80, "max": 0, "unit": "dB" } }
|
|
48
|
+
],
|
|
49
|
+
"tables": { "GATE_LIMITS": { "doc": "…" } },
|
|
50
|
+
"aggregates": { "TREMOLO_TRAVELS": { "doc": "…", "fields": ["rateHz", "depth"] } },
|
|
51
|
+
"constants": { "CHORUS_BASE_MS": { "kind": "scalar", "doc": "…", "unit": "ms", "value": 10 } }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `controls`, in order: a `travel` (one travel) or a `choice` (ordered `ids`, an optional `default` and `labels`).
|
|
56
|
+
Its item name is `<KERNEL>_<NAME>_RANGE` for a travel and `<KERNEL>_<NAME>S` for a choice (`depth` in
|
|
57
|
+
tremolo.json is `TREMOLO_DEPTH_RANGE`, `mode` is `TREMOLO_MODES`); `global` names it when it is anything else.
|
|
58
|
+
A control with a `table` is one field of that table. `"rearms": true` marks a control whose change re-arms the
|
|
59
|
+
kernel's state, so it is not a smooth parameter (the limiter's `lookaheadMs`: omx-dsp's limiter face re-arms
|
|
60
|
+
the state and restarts the gain at unity when the look-ahead moves by a frame). It is a boolean, only on a
|
|
61
|
+
control; it is no item, so the C and TypeScript renders do not carry it.
|
|
62
|
+
- `tables`: a travels table whose fields are the controls that name it, in control order (`GATE_LIMITS`).
|
|
63
|
+
- `aggregates`: a travels table whose fields are references to controls declared once above, listed by name.
|
|
64
|
+
- `constants`: scalars, lists and sheets, as items.
|
|
65
|
+
|
|
66
|
+
`omx-contract validate` refuses any other key, a constant that is a travel or a set, and a kernel file written
|
|
67
|
+
as a flat item map (the 1.x shape). `rates.json` and `primitives.json` stay item maps.
|
|
68
|
+
|
|
69
|
+
## The JSON render's kernels
|
|
70
|
+
|
|
71
|
+
Beside `items`, `share/omx-contract/omx-contract.json` carries `kernels`: for every kernel file, its controls in
|
|
72
|
+
the order the file declares them. A consumer reads control order and kinds here, never from a kernel file.
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
"kernels": {
|
|
76
|
+
"limiter": {
|
|
77
|
+
"controls": [
|
|
78
|
+
{ "name": "ceilingDb", "kind": "travel", "global": "LIMITER_LIMITS", "table": "LIMITER_LIMITS" },
|
|
79
|
+
{ "name": "lookaheadMs", "kind": "travel", "global": "LIMITER_LIMITS", "table": "LIMITER_LIMITS", "rearms": true },
|
|
80
|
+
{ "name": "releaseMs", "kind": "travel", "global": "LIMITER_LIMITS", "table": "LIMITER_LIMITS" }
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"tremolo": {
|
|
84
|
+
"controls": [
|
|
85
|
+
{ "name": "rateHz", "kind": "travel", "global": "TREMOLO_RATE_RANGE" },
|
|
86
|
+
{ "name": "mode", "kind": "choice", "global": "TREMOLO_MODES" }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `kind`: `travel` or `choice`.
|
|
93
|
+
- `global`: the item the control's value lives in: `items[global].value` for a travel or a choice, and
|
|
94
|
+
`items[global].value[name]` for a field of a table, when `table` (the same item) is present.
|
|
95
|
+
- `rearms`: present, and `true`, only on a control that declares it.
|
|
96
|
+
|
|
97
|
+
A kernel with no controls (fbs, mixmatrix, rta) is listed with an empty `controls`.
|
|
98
|
+
|
|
33
99
|
## The CLI
|
|
34
100
|
|
|
35
101
|
```
|
|
@@ -46,9 +112,9 @@ share/omx-contract` and `render --target ts --out test/golden/ts`; CI holds all
|
|
|
46
112
|
|
|
47
113
|
## Consuming it
|
|
48
114
|
|
|
49
|
-
Pin one version (`omx-contract 1.
|
|
115
|
+
Pin one version (`omx-contract 2.1.0` in `.github/pins.txt`) and build against its renders, committing
|
|
50
116
|
none: the installed `omx-contract-devel` / `libomx-contract-dev` at exactly that version
|
|
51
|
-
(`pkg-config --exact-version=1.
|
|
117
|
+
(`pkg-config --exact-version=2.1.0 omx-contract`), else the release's npm tarball, unpacked, and
|
|
52
118
|
`omx-contract render --target c --out build/omx-contract/include`. Include
|
|
53
119
|
`<omxcontract/omx_contract_limits.h>`. A plugin's parameter header is rendered by omx-plugins from its
|
|
54
120
|
declaration, by reference to the kernel travels here (since 1.1.0).
|
package/bin/omx-contract.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
15
15
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
|
-
import { loadData, packageVersion, resolveData } from '../lib/data.mjs';
|
|
18
|
+
import { KERNEL_SECTIONS, isKernelFile, kernelDocOf, loadData, packageVersion, resolveData } from '../lib/data.mjs';
|
|
19
19
|
import { formatDoc } from '../lib/fmt.mjs';
|
|
20
20
|
import { classify, refusal } from '../lib/semver.mjs';
|
|
21
21
|
import { validateData } from '../lib/validate.mjs';
|
|
@@ -122,12 +122,23 @@ function cmdRender(argv) {
|
|
|
122
122
|
return 0;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* The data of a git tag, as `{ rel: text }`. A tag released before 2.0.0 wrote its kernel files as flat item maps;
|
|
127
|
+
* each is restated in the kernel-file shape (kernelDocOf, the inverse of the expansion the loader reads), so the
|
|
128
|
+
* comparison is between the items the two releases declare.
|
|
129
|
+
*/
|
|
126
130
|
function dataAtTag(tag) {
|
|
127
131
|
const git = (...a) => execFileSync('git', ['-C', ROOT, ...a], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
128
132
|
try { git('rev-parse', '--verify', `${tag}^{commit}`); } catch { throw new Usage(`no tag ${tag} in ${ROOT}`); }
|
|
129
133
|
const texts = {};
|
|
130
|
-
for (const rel of git('ls-tree', '-r', '--name-only', tag, '--', 'data').split('\n').filter((p) => p.endsWith('.json')))
|
|
134
|
+
for (const rel of git('ls-tree', '-r', '--name-only', tag, '--', 'data').split('\n').filter((p) => p.endsWith('.json'))) {
|
|
135
|
+
const text = git('show', `${tag}:${rel}`);
|
|
136
|
+
let doc;
|
|
137
|
+
try { doc = JSON.parse(text); } catch { texts[rel] = text; continue; }
|
|
138
|
+
if (!isKernelFile(rel) || KERNEL_SECTIONS.some((k) => k in doc)) { texts[rel] = text; continue; }
|
|
139
|
+
const { $schema, ...items } = doc;
|
|
140
|
+
texts[rel] = JSON.stringify({ $schema, ...kernelDocOf(rel, Object.entries(items)) });
|
|
141
|
+
}
|
|
131
142
|
return texts;
|
|
132
143
|
}
|
|
133
144
|
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "../../schema/omx-contract.schema.json",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
"controls": [
|
|
4
|
+
{
|
|
5
|
+
"name": "pan",
|
|
6
|
+
"kind": "travel",
|
|
7
|
+
"global": "PAN_RANGE",
|
|
8
|
+
"doc": "The PAN travel — −1 (hard left) … 0 (center) … +1 (hard right). A property of the pan law itself, not of any desk, so it is declared once. Continuous: a pan position has no quantum, and panGains clamps to these bounds.",
|
|
9
|
+
"travel": {
|
|
10
|
+
"min": -1,
|
|
11
|
+
"max": 1,
|
|
12
|
+
"step": 0,
|
|
13
|
+
"unit": ""
|
|
14
|
+
},
|
|
15
|
+
"c": {
|
|
16
|
+
"alias": {
|
|
17
|
+
"fact": "pan",
|
|
18
|
+
"field": "pan"
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
]
|
|
19
23
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../schema/omx-contract.schema.json",
|
|
3
|
+
"controls": [
|
|
4
|
+
{
|
|
5
|
+
"name": "mode",
|
|
6
|
+
"kind": "choice",
|
|
7
|
+
"doc": "The side of the threshold the band acts on. Above acts when the band is loud, taming a resonance that only rings when pushed; below acts when it is quiet, lifting a band that drops out. The order is the integer the kernel reads: above is 0, below is 1.",
|
|
8
|
+
"ids": [
|
|
9
|
+
"above",
|
|
10
|
+
"below"
|
|
11
|
+
],
|
|
12
|
+
"default": "above"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "threshold",
|
|
16
|
+
"kind": "travel",
|
|
17
|
+
"doc": "Where the band starts to act, in dB of the band's own level and not the strip's. It is the same fact the de-esser's threshold is, so it carries the same travel.",
|
|
18
|
+
"travel": {
|
|
19
|
+
"min": {
|
|
20
|
+
"ref": "DEESS_THRESHOLD_RANGE.min"
|
|
21
|
+
},
|
|
22
|
+
"max": {
|
|
23
|
+
"ref": "DEESS_THRESHOLD_RANGE.max"
|
|
24
|
+
},
|
|
25
|
+
"step": {
|
|
26
|
+
"ref": "DEESS_THRESHOLD_RANGE.step"
|
|
27
|
+
},
|
|
28
|
+
"unit": "dB",
|
|
29
|
+
"default": {
|
|
30
|
+
"ref": "DEESS_THRESHOLD_RANGE.default"
|
|
31
|
+
},
|
|
32
|
+
"defaultFrom": "desk"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "ratio",
|
|
37
|
+
"kind": "travel",
|
|
38
|
+
"doc": "How hard the band is held once it is past its threshold. 2:1 rather than the de-esser's 4:1, because a dynamic band is corrective, not a limiter for a sibilant.",
|
|
39
|
+
"travel": {
|
|
40
|
+
"min": {
|
|
41
|
+
"ref": "DEESS_RATIO_RANGE.min"
|
|
42
|
+
},
|
|
43
|
+
"max": {
|
|
44
|
+
"ref": "DEESS_RATIO_RANGE.max"
|
|
45
|
+
},
|
|
46
|
+
"step": {
|
|
47
|
+
"ref": "DEESS_RATIO_RANGE.step"
|
|
48
|
+
},
|
|
49
|
+
"unit": "",
|
|
50
|
+
"default": 2,
|
|
51
|
+
"defaultFrom": "desk"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "range",
|
|
56
|
+
"kind": "travel",
|
|
57
|
+
"doc": "The most the band may add to its static gain, signed: below zero the deepest cut, above zero the largest boost. The ceiling of +12 keeps static plus dynamic inside the EQ's own travel. Zero makes the band inert without switching it off.",
|
|
58
|
+
"travel": {
|
|
59
|
+
"min": {
|
|
60
|
+
"ref": "DEESS_RANGE_RANGE.min"
|
|
61
|
+
},
|
|
62
|
+
"max": 12,
|
|
63
|
+
"step": {
|
|
64
|
+
"ref": "DEESS_RANGE_RANGE.step"
|
|
65
|
+
},
|
|
66
|
+
"unit": "dB",
|
|
67
|
+
"default": -6,
|
|
68
|
+
"defaultFrom": "desk"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "attack",
|
|
73
|
+
"kind": "travel",
|
|
74
|
+
"doc": "The detector's attack, in ms. The travel stops at the oversampling floor, below which the band would need the compressor's delayed control path.",
|
|
75
|
+
"travel": {
|
|
76
|
+
"min": {
|
|
77
|
+
"ref": "DETECTOR_OVERSAMPLE_AUTO_MS"
|
|
78
|
+
},
|
|
79
|
+
"max": {
|
|
80
|
+
"ref": "COMP_LIMITS.attackMs.max"
|
|
81
|
+
},
|
|
82
|
+
"step": {
|
|
83
|
+
"ref": "DEESS_ATTACK_RANGE.step"
|
|
84
|
+
},
|
|
85
|
+
"unit": "ms",
|
|
86
|
+
"default": 5,
|
|
87
|
+
"defaultFrom": "desk"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "release",
|
|
92
|
+
"kind": "travel",
|
|
93
|
+
"doc": "The detector's release, in ms. 100 ms is long enough not to modulate a 40 Hz band inside four cycles.",
|
|
94
|
+
"travel": {
|
|
95
|
+
"min": {
|
|
96
|
+
"ref": "DEESS_RELEASE_RANGE.min"
|
|
97
|
+
},
|
|
98
|
+
"max": {
|
|
99
|
+
"ref": "COMP_LIMITS.releaseMs.max"
|
|
100
|
+
},
|
|
101
|
+
"step": {
|
|
102
|
+
"ref": "DEESS_RELEASE_RANGE.step"
|
|
103
|
+
},
|
|
104
|
+
"unit": "ms",
|
|
105
|
+
"default": 100,
|
|
106
|
+
"defaultFrom": "desk"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"constants": {
|
|
111
|
+
"BAND_DYN_KNEE_DB": {
|
|
112
|
+
"kind": "scalar",
|
|
113
|
+
"doc": "The soft knee of the band's gain computer, in dB. One knee for the one band-dynamics word, shared by every stage that runs it, the de-esser included: a second spelling of 6 would be a second place where the reduction starts.",
|
|
114
|
+
"unit": "dB",
|
|
115
|
+
"value": 6
|
|
116
|
+
},
|
|
117
|
+
"BAND_DYN_MAX_BANDS": {
|
|
118
|
+
"kind": "scalar",
|
|
119
|
+
"doc": "How many dynamic bands a channel may hold. A dynamic band is an operator band, so the capacity is the operator's reserve, read and not typed again.",
|
|
120
|
+
"unit": "",
|
|
121
|
+
"value": {
|
|
122
|
+
"ref": "OPERATOR_EQ_BANDS_RESERVE"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"DETECTOR_OVERSAMPLE_AUTO_MS": {
|
|
126
|
+
"kind": "scalar",
|
|
127
|
+
"doc": "The shortest attack, in ms, at which the compressor's detector does not alias. Below it the compressor engages its 4x control path, which adds 72 samples of latency; a zero-latency band has no such path, so its attack travel stops at this floor.",
|
|
128
|
+
"unit": "ms",
|
|
129
|
+
"value": 0.5
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
package/data/kernels/chorus.json
CHANGED
|
@@ -1,85 +1,94 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "../../schema/omx-contract.schema.json",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
"controls": [
|
|
4
|
+
{
|
|
5
|
+
"name": "spread",
|
|
6
|
+
"kind": "travel",
|
|
7
|
+
"doc": "The chorus's right-leg phase offset, in TURNS of its one oscillator (§2, §3a; the native-FX catalogue's M32 Dimension row). The right leg's voice k reads the LFO at k/N + spread: 0 is both legs one phase (the stage without the field, bit for bit), 0.25 quadrature, 0.5 the Dimension's opposed sweep. A fraction of a cycle carries no engineering symbol, so the unit is ''.",
|
|
8
|
+
"travel": {
|
|
9
|
+
"min": 0,
|
|
10
|
+
"max": 0.5,
|
|
11
|
+
"step": 0.01,
|
|
12
|
+
"unit": "",
|
|
13
|
+
"default": 0,
|
|
14
|
+
"defaultFrom": "desk"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "rate",
|
|
19
|
+
"kind": "travel",
|
|
20
|
+
"doc": "The chorus sweep's speed (§3a). Above ~3 Hz it stops being a chorus and becomes a vibrato.",
|
|
21
|
+
"travel": {
|
|
22
|
+
"min": 0.05,
|
|
23
|
+
"max": 8,
|
|
24
|
+
"step": 0.01,
|
|
25
|
+
"unit": "Hz",
|
|
26
|
+
"default": 0.6,
|
|
27
|
+
"defaultFrom": "desk"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "depth",
|
|
32
|
+
"kind": "travel",
|
|
33
|
+
"doc": "The chorus's peak-to-peak excursion above its declared 10 ms base (§3a).",
|
|
34
|
+
"travel": {
|
|
35
|
+
"min": 0,
|
|
36
|
+
"max": 12,
|
|
37
|
+
"step": 0.1,
|
|
38
|
+
"unit": "ms",
|
|
39
|
+
"default": 4,
|
|
40
|
+
"defaultFrom": "desk"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "voices",
|
|
45
|
+
"kind": "travel",
|
|
46
|
+
"doc": "The chorus's tap count — one oscillator read at k/N (§3a).",
|
|
47
|
+
"travel": {
|
|
48
|
+
"min": 1,
|
|
49
|
+
"max": 4,
|
|
50
|
+
"step": 1,
|
|
51
|
+
"unit": "",
|
|
52
|
+
"default": 3,
|
|
53
|
+
"defaultFrom": "desk"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "mix",
|
|
58
|
+
"kind": "travel",
|
|
59
|
+
"doc": "The chorus's wet/dry, as an instance of DRIVE_MIX_RANGE — the same percent travel, its own come-up value: 35 %, a colour under the dry on a strip (§3a). The REVERB_LOWCUT_RANGE-over- REVERB_CUT_RANGE shape: one travel, one declared default per field that reads it.",
|
|
60
|
+
"travel": {
|
|
61
|
+
"min": 0,
|
|
62
|
+
"max": 100,
|
|
63
|
+
"step": 0.1,
|
|
64
|
+
"unit": "%",
|
|
65
|
+
"default": 35,
|
|
66
|
+
"defaultFrom": "desk"
|
|
67
|
+
}
|
|
13
68
|
}
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"min": 0.05,
|
|
20
|
-
"max": 8,
|
|
21
|
-
"step": 0.01,
|
|
22
|
-
"unit": "Hz",
|
|
23
|
-
"default": 0.6,
|
|
24
|
-
"defaultFrom": "desk"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"CHORUS_DEPTH_RANGE": {
|
|
28
|
-
"kind": "travels",
|
|
29
|
-
"doc": "The chorus's peak-to-peak excursion above its declared 10 ms base (§3a).",
|
|
30
|
-
"travel": {
|
|
31
|
-
"min": 0,
|
|
32
|
-
"max": 12,
|
|
33
|
-
"step": 0.1,
|
|
69
|
+
],
|
|
70
|
+
"constants": {
|
|
71
|
+
"CHORUS_BASE_MS": {
|
|
72
|
+
"kind": "scalar",
|
|
73
|
+
"doc": "The chorus's base delay, ms: the voices swing around it by up to the depth.",
|
|
34
74
|
"unit": "ms",
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"kind": "travels",
|
|
53
|
-
"doc": "The chorus's wet/dry, as an instance of DRIVE_MIX_RANGE — the same percent travel, its own come-up value: 35 %, a colour under the dry on a strip (§3a). The REVERB_LOWCUT_RANGE-over- REVERB_CUT_RANGE shape: one travel, one declared default per field that reads it.",
|
|
54
|
-
"travel": {
|
|
55
|
-
"min": 0,
|
|
56
|
-
"max": 100,
|
|
57
|
-
"step": 0.1,
|
|
58
|
-
"unit": "%",
|
|
59
|
-
"default": 35,
|
|
60
|
-
"defaultFrom": "desk"
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"CHORUS_BASE_MS": {
|
|
64
|
-
"kind": "scalar",
|
|
65
|
-
"doc": "The chorus's base delay, ms: the voices swing around it by up to the depth.",
|
|
66
|
-
"unit": "ms",
|
|
67
|
-
"value": 10
|
|
68
|
-
},
|
|
69
|
-
"CHORUS_MAX_MS": {
|
|
70
|
-
"kind": "scalar",
|
|
71
|
-
"doc": "The longest delay a chorus voice reaches, rounded up to whole ms: the base delay plus the depth maximum. The delay line is sized from it.",
|
|
72
|
-
"unit": "ms",
|
|
73
|
-
"value": {
|
|
74
|
-
"derive": "ceilSum",
|
|
75
|
-
"of": [
|
|
76
|
-
{
|
|
77
|
-
"ref": "CHORUS_BASE_MS"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"ref": "CHORUS_DEPTH_RANGE.max"
|
|
81
|
-
}
|
|
82
|
-
]
|
|
75
|
+
"value": 10
|
|
76
|
+
},
|
|
77
|
+
"CHORUS_MAX_MS": {
|
|
78
|
+
"kind": "scalar",
|
|
79
|
+
"doc": "The longest delay a chorus voice reaches, rounded up to whole ms: the base delay plus the depth maximum. The delay line is sized from it.",
|
|
80
|
+
"unit": "ms",
|
|
81
|
+
"value": {
|
|
82
|
+
"derive": "ceilSum",
|
|
83
|
+
"of": [
|
|
84
|
+
{
|
|
85
|
+
"ref": "CHORUS_BASE_MS"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"ref": "CHORUS_DEPTH_RANGE.max"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
}
|
package/data/kernels/comp.json
CHANGED
|
@@ -1,58 +1,89 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "../../schema/omx-contract.schema.json",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
3
|
+
"controls": [
|
|
4
|
+
{
|
|
5
|
+
"name": "thresholdDb",
|
|
6
|
+
"kind": "travel",
|
|
7
|
+
"table": "COMP_LIMITS",
|
|
8
|
+
"travel": {
|
|
8
9
|
"min": -60,
|
|
9
10
|
"max": 0,
|
|
10
11
|
"step": 1,
|
|
11
12
|
"unit": "dB",
|
|
12
13
|
"default": -18,
|
|
13
14
|
"defaultFrom": "desk"
|
|
14
|
-
}
|
|
15
|
-
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "ratio",
|
|
19
|
+
"kind": "travel",
|
|
20
|
+
"table": "COMP_LIMITS",
|
|
21
|
+
"travel": {
|
|
16
22
|
"min": 1,
|
|
17
23
|
"max": 20,
|
|
18
24
|
"step": 0.5,
|
|
19
25
|
"unit": "",
|
|
20
26
|
"default": 4,
|
|
21
27
|
"defaultFrom": "desk"
|
|
22
|
-
}
|
|
23
|
-
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "kneeDb",
|
|
32
|
+
"kind": "travel",
|
|
33
|
+
"table": "COMP_LIMITS",
|
|
34
|
+
"travel": {
|
|
24
35
|
"min": 0,
|
|
25
36
|
"max": 24,
|
|
26
37
|
"step": 0.5,
|
|
27
38
|
"unit": "dB",
|
|
28
39
|
"default": 6,
|
|
29
40
|
"defaultFrom": "desk"
|
|
30
|
-
}
|
|
31
|
-
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "attackMs",
|
|
45
|
+
"kind": "travel",
|
|
46
|
+
"table": "COMP_LIMITS",
|
|
47
|
+
"travel": {
|
|
32
48
|
"min": 0.1,
|
|
33
49
|
"max": 100,
|
|
34
50
|
"step": 1,
|
|
35
51
|
"unit": "ms",
|
|
36
52
|
"default": 5,
|
|
37
53
|
"defaultFrom": "desk"
|
|
38
|
-
}
|
|
39
|
-
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "releaseMs",
|
|
58
|
+
"kind": "travel",
|
|
59
|
+
"table": "COMP_LIMITS",
|
|
60
|
+
"travel": {
|
|
40
61
|
"min": 5,
|
|
41
62
|
"max": 3000,
|
|
42
63
|
"step": 10,
|
|
43
64
|
"unit": "ms",
|
|
44
65
|
"default": 200,
|
|
45
66
|
"defaultFrom": "desk"
|
|
46
|
-
}
|
|
47
|
-
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "makeupDb",
|
|
71
|
+
"kind": "travel",
|
|
72
|
+
"table": "COMP_LIMITS",
|
|
73
|
+
"travel": {
|
|
48
74
|
"min": 0,
|
|
49
75
|
"max": 24,
|
|
50
76
|
"step": 0.5,
|
|
51
77
|
"unit": "dB",
|
|
52
78
|
"default": 0,
|
|
53
79
|
"defaultFrom": "desk"
|
|
54
|
-
}
|
|
55
|
-
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "mixPct",
|
|
84
|
+
"kind": "travel",
|
|
85
|
+
"table": "COMP_LIMITS",
|
|
86
|
+
"travel": {
|
|
56
87
|
"min": 0,
|
|
57
88
|
"max": 100,
|
|
58
89
|
"step": 5,
|
|
@@ -61,19 +92,26 @@
|
|
|
61
92
|
"defaultFrom": "desk"
|
|
62
93
|
}
|
|
63
94
|
}
|
|
95
|
+
],
|
|
96
|
+
"tables": {
|
|
97
|
+
"COMP_LIMITS": {
|
|
98
|
+
"doc": "The legal range of each compressor field (engineering units). The widget clamps drags to these, `normalizeComp` enforces them, the row's codec refuses by them and the travel sheet publishes them. `default`/`defaultFrom` are the come-up values `defaultCompState` seeds a fresh strip with, read BACK from here rather than restated there."
|
|
99
|
+
}
|
|
64
100
|
},
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
"constants": {
|
|
102
|
+
"PROGRAM_RELEASE": {
|
|
103
|
+
"kind": "sheet",
|
|
104
|
+
"doc": "The program-dependent release's model constants — the LA-2A's opto cell as DATA (`docs/design/specs/2026-09-26-compressor-models.md` §3b). `chargeMs`/`dischargeMs` are the word's memory constants, shared by every profile; each profile's `fastMs`, `slowMinMs`, `slowMaxMs` and `share` fit the unit's published two-stage recovery. Not operator controls: generated into `omx_contract_limits.h` as `OMX_PROGRAM_RELEASE_*`, which libomxdsp's `omx_env_program_release` reads.",
|
|
105
|
+
"value": {
|
|
106
|
+
"chargeMs": 1000,
|
|
107
|
+
"dischargeMs": 5000,
|
|
108
|
+
"profiles": {
|
|
109
|
+
"opto": {
|
|
110
|
+
"fastMs": 70,
|
|
111
|
+
"slowMinMs": 110,
|
|
112
|
+
"slowMaxMs": 1100,
|
|
113
|
+
"share": 0.5
|
|
114
|
+
}
|
|
77
115
|
}
|
|
78
116
|
}
|
|
79
117
|
}
|