@openmixer/omx-contract 0.0.0 → 1.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/LICENSE +674 -0
- package/README.md +53 -5
- package/bin/omx-contract.mjs +175 -0
- package/data/kernels/chorus.json +15 -0
- package/data/kernels/comp.json +81 -0
- package/data/kernels/deesser.json +93 -0
- package/data/kernels/delay.json +66 -0
- package/data/kernels/drive.json +12 -0
- package/data/kernels/eq.json +59 -0
- package/data/kernels/flanger.json +51 -0
- package/data/kernels/gate.json +81 -0
- package/data/kernels/geq.json +33 -0
- package/data/kernels/limiter.json +33 -0
- package/data/kernels/phaser.json +109 -0
- package/data/kernels/pitch.json +35 -0
- package/data/kernels/reverb.json +15 -0
- package/data/kernels/rotary.json +186 -0
- package/data/kernels/transient.json +73 -0
- package/data/kernels/tremolo.json +54 -0
- package/data/primitives.json +47 -0
- package/data/rates.json +30 -0
- package/dist/ts/omx-contract.d.ts +1071 -0
- package/dist/ts/omx-contract.js +1117 -0
- package/include/omxcontract/omx_contract_limits.h +381 -0
- package/lib/data.mjs +159 -0
- package/lib/fmt.mjs +61 -0
- package/lib/semver.mjs +125 -0
- package/lib/validate.mjs +92 -0
- package/package.json +44 -4
- package/render/c/contract-limits-render.mjs +370 -0
- package/render/c/omx_plugin_param.h +25 -0
- package/render/c/params-generate.mjs +83 -0
- package/render/c.mjs +94 -0
- package/render/doors.mjs +63 -0
- package/render/json.mjs +16 -0
- package/render/ts.mjs +41 -0
- package/schema/omx-contract.schema.json +164 -0
- package/share/omx-contract/omx-contract.json +1202 -0
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
|
+
// GENERATED by `omx-contract render --target ts` from FreeMixer/omx-contract's data/ — do not edit.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 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 ''.
|
|
7
|
+
* @see data/kernels/chorus.json
|
|
8
|
+
*/
|
|
9
|
+
export const CHORUS_SPREAD_RANGE = {
|
|
10
|
+
"min": 0,
|
|
11
|
+
"max": 0.5,
|
|
12
|
+
"step": 0.01,
|
|
13
|
+
"unit": "",
|
|
14
|
+
"default": 0,
|
|
15
|
+
"defaultFrom": "desk"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 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.
|
|
20
|
+
* @see data/kernels/comp.json
|
|
21
|
+
*/
|
|
22
|
+
export const COMP_LIMITS = {
|
|
23
|
+
"thresholdDb": {
|
|
24
|
+
"min": -60,
|
|
25
|
+
"max": 0,
|
|
26
|
+
"step": 1,
|
|
27
|
+
"unit": "dB",
|
|
28
|
+
"default": -18,
|
|
29
|
+
"defaultFrom": "desk"
|
|
30
|
+
},
|
|
31
|
+
"ratio": {
|
|
32
|
+
"min": 1,
|
|
33
|
+
"max": 20,
|
|
34
|
+
"step": 0.5,
|
|
35
|
+
"unit": "",
|
|
36
|
+
"default": 4,
|
|
37
|
+
"defaultFrom": "desk"
|
|
38
|
+
},
|
|
39
|
+
"kneeDb": {
|
|
40
|
+
"min": 0,
|
|
41
|
+
"max": 24,
|
|
42
|
+
"step": 0.5,
|
|
43
|
+
"unit": "dB",
|
|
44
|
+
"default": 6,
|
|
45
|
+
"defaultFrom": "desk"
|
|
46
|
+
},
|
|
47
|
+
"attackMs": {
|
|
48
|
+
"min": 0.1,
|
|
49
|
+
"max": 100,
|
|
50
|
+
"step": 1,
|
|
51
|
+
"unit": "ms",
|
|
52
|
+
"default": 5,
|
|
53
|
+
"defaultFrom": "desk"
|
|
54
|
+
},
|
|
55
|
+
"releaseMs": {
|
|
56
|
+
"min": 5,
|
|
57
|
+
"max": 3000,
|
|
58
|
+
"step": 10,
|
|
59
|
+
"unit": "ms",
|
|
60
|
+
"default": 200,
|
|
61
|
+
"defaultFrom": "desk"
|
|
62
|
+
},
|
|
63
|
+
"makeupDb": {
|
|
64
|
+
"min": 0,
|
|
65
|
+
"max": 24,
|
|
66
|
+
"step": 0.5,
|
|
67
|
+
"unit": "dB",
|
|
68
|
+
"default": 0,
|
|
69
|
+
"defaultFrom": "desk"
|
|
70
|
+
},
|
|
71
|
+
"mixPct": {
|
|
72
|
+
"min": 0,
|
|
73
|
+
"max": 100,
|
|
74
|
+
"step": 5,
|
|
75
|
+
"unit": "%",
|
|
76
|
+
"default": 100,
|
|
77
|
+
"defaultFrom": "desk"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 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.
|
|
83
|
+
* @see data/kernels/comp.json
|
|
84
|
+
*/
|
|
85
|
+
export const PROGRAM_RELEASE = {
|
|
86
|
+
"chargeMs": 1000,
|
|
87
|
+
"dischargeMs": 5000,
|
|
88
|
+
"profiles": {
|
|
89
|
+
"opto": {
|
|
90
|
+
"fastMs": 70,
|
|
91
|
+
"slowMinMs": 110,
|
|
92
|
+
"slowMaxMs": 1100,
|
|
93
|
+
"share": 0.5
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The detector band's CENTRE, in Hz. Sibilance lives at 5–10 kHz on most voices; 7 kHz with the default width covers 4 950–9 900 Hz, which is where an "s" and a "t" sit on a close vocal mic. The travel is wider than the come-up on both sides on purpose: a bright female vocal wants the band up near 9 kHz and a chesty male one down at 5, and a surface narrower than the row removes reach the console genuinely has (the same reasoning core's `REVERB_CUT_RANGE` records).
|
|
100
|
+
* @see data/kernels/deesser.json
|
|
101
|
+
*/
|
|
102
|
+
export const DEESS_FREQ_RANGE = {
|
|
103
|
+
"min": 2000,
|
|
104
|
+
"max": 16000,
|
|
105
|
+
"step": 10,
|
|
106
|
+
"unit": "Hz",
|
|
107
|
+
"default": 7000,
|
|
108
|
+
"defaultFrom": "desk"
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The band's WIDTH, in OCTAVES around the centre: the edges are `f / 2^(w/2)` and `f · 2^(w/2)`, which is what makes the centre the geometric middle at every width. Octaves and not a Q, because a Q is a property of one section and this band is TWO (a highpass and a lowpass, `mix_deesser.h`) — an operator asked to dial a Q would be dialling a number neither filter carries. One octave is the classic de-esser band: wide enough for a sibilant that moves with the vowel, narrow enough that a 1 kHz vowel sits 27.8 dB down the skirt.
|
|
113
|
+
* @see data/kernels/deesser.json
|
|
114
|
+
*/
|
|
115
|
+
export const DEESS_WIDTH_RANGE = {
|
|
116
|
+
"min": 0.25,
|
|
117
|
+
"max": 4,
|
|
118
|
+
"step": 0.05,
|
|
119
|
+
"unit": "oct",
|
|
120
|
+
"default": 1,
|
|
121
|
+
"defaultFrom": "desk"
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Where reduction starts — the level of the BAND, not of the strip, which is why the come-up is so much lower than the compressor's. A vocal peaking −12 dBFS carries roughly −30 dBFS in a one-octave band at 7 kHz on its sibilants and under −40 on its vowels, so −30 sits between the two and the stage opens on an "s" and stays shut on an "ah".
|
|
126
|
+
* @see data/kernels/deesser.json
|
|
127
|
+
*/
|
|
128
|
+
export const DEESS_THRESHOLD_RANGE = {
|
|
129
|
+
"min": -60,
|
|
130
|
+
"max": 0,
|
|
131
|
+
"step": 0.5,
|
|
132
|
+
"unit": "dB",
|
|
133
|
+
"default": -30,
|
|
134
|
+
"defaultFrom": "desk"
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* How hard the band is held once it is over the threshold. 4:1 is the textbook de-esser ratio: firm on the "s", short of a limiter.
|
|
139
|
+
* @see data/kernels/deesser.json
|
|
140
|
+
*/
|
|
141
|
+
export const DEESS_RATIO_RANGE = {
|
|
142
|
+
"min": 1,
|
|
143
|
+
"max": 20,
|
|
144
|
+
"step": 0.1,
|
|
145
|
+
"unit": "",
|
|
146
|
+
"default": 4,
|
|
147
|
+
"defaultFrom": "desk"
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The FLOOR on the reduction, in dB and never positive — the deepest cut the stage may reach however loud the sibilant is (`mix_deesser.h`'s clamp, §4c). 12 dB tames a hard sibilant; past that a voice starts to lisp, and a de-esser with NO floor turns a shouted "s" into a hole. The ceiling is 0, which switches the reduction off without switching the stage off — a legitimate thing to want while dialling the band in by ear.
|
|
152
|
+
* @see data/kernels/deesser.json
|
|
153
|
+
*/
|
|
154
|
+
export const DEESS_RANGE_RANGE = {
|
|
155
|
+
"min": -24,
|
|
156
|
+
"max": 0,
|
|
157
|
+
"step": 0.5,
|
|
158
|
+
"unit": "dB",
|
|
159
|
+
"default": -12,
|
|
160
|
+
"defaultFrom": "desk"
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The detector's ATTACK, in ms. A sibilant's onset is a few milliseconds; 1 ms catches it without the detector chattering on the band's own waveform.
|
|
165
|
+
* @see data/kernels/deesser.json
|
|
166
|
+
*/
|
|
167
|
+
export const DEESS_ATTACK_RANGE = {
|
|
168
|
+
"min": 0.1,
|
|
169
|
+
"max": 50,
|
|
170
|
+
"step": 0.1,
|
|
171
|
+
"unit": "ms",
|
|
172
|
+
"default": 1,
|
|
173
|
+
"defaultFrom": "desk"
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The detector's RELEASE, in ms. 60 ms is long enough not to modulate inside one "s" and short enough to be open again for the next syllable.
|
|
178
|
+
* @see data/kernels/deesser.json
|
|
179
|
+
*/
|
|
180
|
+
export const DEESS_RELEASE_RANGE = {
|
|
181
|
+
"min": 5,
|
|
182
|
+
"max": 500,
|
|
183
|
+
"step": 1,
|
|
184
|
+
"unit": "ms",
|
|
185
|
+
"default": 60,
|
|
186
|
+
"defaultFrom": "desk"
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The knee, in dB — a CONSTANT of the stage and not a row field (`2026-09-22-native-deesser-stage.md` §4b): a de-esser without a soft knee latches audibly on the first sibilant of a phrase, and no other value of this number is a de-esser anyone would dial. The controller pushes it to the node and this is the same number; a second spelling would be a picture that disagrees with the audio about where the reduction starts.
|
|
191
|
+
* @see data/kernels/deesser.json
|
|
192
|
+
*/
|
|
193
|
+
export const DEESS_KNEE_DB = 6;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The FX delay's time travel. `max` is the NATIVE ring's ceiling — the same number as `OMX_FXDELAY_MAX_MS` in `omx_delay.h`, pinned to it by `output-delay-ceiling.test.ts`. Distinct from the per-route ALIGNMENT delay (`OUTPUT_DELAY_RANGE`, 1 s, `OMX_DELAY_MAX_MS`): one is a musical echo with feedback and mix, the other is a pure latency. Two facts, two rings, two ceilings — sharing the word "delay" is why they keep being confused for each other.
|
|
197
|
+
* @see data/kernels/delay.json
|
|
198
|
+
*/
|
|
199
|
+
export const FX_DELAY_TIME_RANGE = {
|
|
200
|
+
"min": 0,
|
|
201
|
+
"max": 2000,
|
|
202
|
+
"step": 1,
|
|
203
|
+
"unit": "ms",
|
|
204
|
+
"default": 300,
|
|
205
|
+
"defaultFrom": "desk"
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The FX delay's REGENERATION travel — how much of the wet tail feeds back in. Stops short of 1: unity feedback is a runaway the kernel hard-clamps anyway, so offering it would draw travel the DSP refuses. Not NORMALIZED_RANGE for exactly that reason — it is a 0..1 quantity whose top the DSP does not accept, and a control must not offer a value its actuator will eat.
|
|
210
|
+
* @see data/kernels/delay.json
|
|
211
|
+
*/
|
|
212
|
+
export const FX_DELAY_FEEDBACK_RANGE = {
|
|
213
|
+
"min": 0,
|
|
214
|
+
"max": 0.99,
|
|
215
|
+
"step": 0,
|
|
216
|
+
"unit": "",
|
|
217
|
+
"default": 0.3,
|
|
218
|
+
"defaultFrom": "desk"
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The FX delay's TONE — a NORMALIZED_RANGE instance with its own come-up value, matching `DEFAULT_DELAY_STATE.tone`. See REVERB_SIZE_RANGE for why this is a named constant rather than a default on the shared shape.
|
|
223
|
+
* @see data/kernels/delay.json
|
|
224
|
+
*/
|
|
225
|
+
export const DELAY_TONE_RANGE = {
|
|
226
|
+
"min": 0,
|
|
227
|
+
"max": 1,
|
|
228
|
+
"step": 0,
|
|
229
|
+
"unit": "",
|
|
230
|
+
"default": 0.3,
|
|
231
|
+
"defaultFrom": "desk"
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The FX DELAY's wet/dry — the same split as REVERB_MIX_RANGE, its own declaration.
|
|
236
|
+
* @see data/kernels/delay.json
|
|
237
|
+
*/
|
|
238
|
+
export const DELAY_MIX_RANGE = {
|
|
239
|
+
"min": 0,
|
|
240
|
+
"max": 1,
|
|
241
|
+
"step": 0,
|
|
242
|
+
"unit": "",
|
|
243
|
+
"default": 0.3,
|
|
244
|
+
"defaultFrom": "desk",
|
|
245
|
+
"byKind": {
|
|
246
|
+
"fxReturn": 1
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* The FX delay's PING-PONG come-up value — a toggle, so it has no travel, only this. Declared here rather than as a literal in the server's `DEFAULT_DELAY_STATE` (which now reads it) so the DPF plugin generator (`packages/omx-plugins/tools/params-gen.mjs`, `2026-09-25-omx-plugins-dpf.md` §3a) reads the same fact the desk seeds a fresh strip with.
|
|
252
|
+
* @see data/kernels/delay.json
|
|
253
|
+
*/
|
|
254
|
+
export const FX_DELAY_PINGPONG_DEFAULT = false;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The bias at `character = +1`, in the SHAPER's own input units — `OMX_DRIVE_BIAS_MAX`. DERIVED, not chosen (§4b). In the saturating limit every knee tends to `sgn`, the output is a rectangular wave of duty `d = ½ + asin(b)/π`, and its `n`th harmonic goes as `sin(nπd)/n` — so `|H2|` is maximal at `d = ¾`, which is `b = sin(π/4) = 1/√2` exactly. Measured too: the H2-maximising bias for a full-scale sine at unity drive is 0.712 (soft), 0.658 (tape), 0.645 (tube), and this one value costs 0.000 / 0.035 / 0.088 dB of H2 against each curve's own optimum. ONE normalised bias for all three knees, not three.
|
|
258
|
+
* @see data/kernels/drive.json
|
|
259
|
+
*/
|
|
260
|
+
export const DRIVE_BIAS_MAX = 0.7071067811865476;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* The maximum number of parametric bands a channel EQ may hold — the operator can add up to this many. Generated into `omx_contract_limits.h` as `OMX_EQ_MAX_BANDS`, which `omx_biquad.h` reads (`output-delay-ceiling.test.ts` reads the header): the native lane allocates exactly this many biquad slots per channel and silently clamps above it, so the model, the wire, and the DSP agree by test, not by memory.
|
|
264
|
+
* @see data/kernels/eq.json
|
|
265
|
+
*/
|
|
266
|
+
export const EQ_MAX_BANDS = 24;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The standard ISO 266 one-third-octave centre frequencies, 20 Hz–20 kHz (31 bands) — the classic 31-band graphic-EQ layout's centres.
|
|
270
|
+
* @see data/kernels/eq.json
|
|
271
|
+
*/
|
|
272
|
+
export const ISO_THIRD_OCTAVE_CENTRES_HZ = [
|
|
273
|
+
20,
|
|
274
|
+
25,
|
|
275
|
+
31.5,
|
|
276
|
+
40,
|
|
277
|
+
50,
|
|
278
|
+
63,
|
|
279
|
+
80,
|
|
280
|
+
100,
|
|
281
|
+
125,
|
|
282
|
+
160,
|
|
283
|
+
200,
|
|
284
|
+
250,
|
|
285
|
+
315,
|
|
286
|
+
400,
|
|
287
|
+
500,
|
|
288
|
+
630,
|
|
289
|
+
800,
|
|
290
|
+
1000,
|
|
291
|
+
1250,
|
|
292
|
+
1600,
|
|
293
|
+
2000,
|
|
294
|
+
2500,
|
|
295
|
+
3150,
|
|
296
|
+
4000,
|
|
297
|
+
5000,
|
|
298
|
+
6300,
|
|
299
|
+
8000,
|
|
300
|
+
10000,
|
|
301
|
+
12500,
|
|
302
|
+
16000,
|
|
303
|
+
20000
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The graphic EQ's section count: DERIVED from ISO_THIRD_OCTAVE_CENTRES_HZ, never a second literal. Generated into `omx_contract_limits.h` as `OMX_GEQ_BANDS`, which sizes `mix_geq.h`'s section array — its OWN budget, never a claimant on EQ_MAX_BANDS (graphic-eq-31 spec §3, roster ruling 3).
|
|
308
|
+
* @see data/kernels/eq.json
|
|
309
|
+
*/
|
|
310
|
+
export const GEQ_BANDS = 31;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* The flanger's jet sweep (§3b).
|
|
314
|
+
* @see data/kernels/flanger.json
|
|
315
|
+
*/
|
|
316
|
+
export const FLANGER_RATE_RANGE = {
|
|
317
|
+
"min": 0.05,
|
|
318
|
+
"max": 5,
|
|
319
|
+
"step": 0.01,
|
|
320
|
+
"unit": "Hz",
|
|
321
|
+
"default": 0.25,
|
|
322
|
+
"defaultFrom": "desk"
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* The flanger's peak-to-peak excursion above its declared 0.5 ms base (§3b).
|
|
327
|
+
* @see data/kernels/flanger.json
|
|
328
|
+
*/
|
|
329
|
+
export const FLANGER_DEPTH_RANGE = {
|
|
330
|
+
"min": 0,
|
|
331
|
+
"max": 5,
|
|
332
|
+
"step": 0.1,
|
|
333
|
+
"unit": "ms",
|
|
334
|
+
"default": 2,
|
|
335
|
+
"defaultFrom": "desk"
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* The flanger's SIGNED resonance (§3b). The ±0.95 clamp is DECLARED here — a PATCH asking for 1.0 is answered by this travel — and the kernel's own clamp is the enforcement half.
|
|
340
|
+
* @see data/kernels/flanger.json
|
|
341
|
+
*/
|
|
342
|
+
export const FLANGER_FEEDBACK_RANGE = {
|
|
343
|
+
"min": -0.95,
|
|
344
|
+
"max": 0.95,
|
|
345
|
+
"step": 0.01,
|
|
346
|
+
"unit": "",
|
|
347
|
+
"default": 0.6,
|
|
348
|
+
"defaultFrom": "desk"
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* The flanger's wet/dry, as an instance of DRIVE_MIX_RANGE: 50 %, equal dry and wet, where the comb is deepest (§3b).
|
|
353
|
+
* @see data/kernels/flanger.json
|
|
354
|
+
*/
|
|
355
|
+
export const FLANGER_MIX_RANGE = {
|
|
356
|
+
"min": 0,
|
|
357
|
+
"max": 100,
|
|
358
|
+
"step": 0.1,
|
|
359
|
+
"unit": "%",
|
|
360
|
+
"default": 50,
|
|
361
|
+
"defaultFrom": "desk"
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The legal range of each gate field (engineering units). The widget clamps drags to these, `normalizeGate` enforces them, the row's codec refuses by them and the travel sheet publishes them — one source of truth. `rangeDb` reaches GATE_RANGE_FLOOR_DB, shown as "−∞" in readouts (a gate fully closed is silence).
|
|
366
|
+
* @see data/kernels/gate.json
|
|
367
|
+
*/
|
|
368
|
+
export const GATE_LIMITS = {
|
|
369
|
+
"thresholdDb": {
|
|
370
|
+
"min": -80,
|
|
371
|
+
"max": 0,
|
|
372
|
+
"step": 1,
|
|
373
|
+
"unit": "dB",
|
|
374
|
+
"default": -40,
|
|
375
|
+
"defaultFrom": "desk"
|
|
376
|
+
},
|
|
377
|
+
"rangeDb": {
|
|
378
|
+
"min": -90,
|
|
379
|
+
"max": 0,
|
|
380
|
+
"step": 1,
|
|
381
|
+
"unit": "dB",
|
|
382
|
+
"default": -90,
|
|
383
|
+
"defaultFrom": "desk"
|
|
384
|
+
},
|
|
385
|
+
"kneeStartDb": {
|
|
386
|
+
"min": -80,
|
|
387
|
+
"max": 0,
|
|
388
|
+
"step": 1,
|
|
389
|
+
"unit": "dB",
|
|
390
|
+
"default": -43,
|
|
391
|
+
"defaultFrom": "desk"
|
|
392
|
+
},
|
|
393
|
+
"kneeEndDb": {
|
|
394
|
+
"min": -80,
|
|
395
|
+
"max": 0,
|
|
396
|
+
"step": 1,
|
|
397
|
+
"unit": "dB",
|
|
398
|
+
"default": -37,
|
|
399
|
+
"defaultFrom": "desk"
|
|
400
|
+
},
|
|
401
|
+
"attackMs": {
|
|
402
|
+
"min": 0,
|
|
403
|
+
"max": 500,
|
|
404
|
+
"step": 0.5,
|
|
405
|
+
"unit": "ms",
|
|
406
|
+
"default": 1,
|
|
407
|
+
"defaultFrom": "desk"
|
|
408
|
+
},
|
|
409
|
+
"holdMs": {
|
|
410
|
+
"min": 0,
|
|
411
|
+
"max": 2000,
|
|
412
|
+
"step": 5,
|
|
413
|
+
"unit": "ms",
|
|
414
|
+
"default": 10,
|
|
415
|
+
"defaultFrom": "desk"
|
|
416
|
+
},
|
|
417
|
+
"releaseMs": {
|
|
418
|
+
"min": 0,
|
|
419
|
+
"max": 5000,
|
|
420
|
+
"step": 10,
|
|
421
|
+
"unit": "ms",
|
|
422
|
+
"default": 100,
|
|
423
|
+
"defaultFrom": "desk"
|
|
424
|
+
},
|
|
425
|
+
"hysteresisDb": {
|
|
426
|
+
"min": 0,
|
|
427
|
+
"max": 24,
|
|
428
|
+
"step": 0.5,
|
|
429
|
+
"unit": "dB",
|
|
430
|
+
"default": 3,
|
|
431
|
+
"defaultFrom": "desk"
|
|
432
|
+
},
|
|
433
|
+
"ratio": {
|
|
434
|
+
"min": 1,
|
|
435
|
+
"max": 100,
|
|
436
|
+
"step": 1,
|
|
437
|
+
"unit": "",
|
|
438
|
+
"default": 16,
|
|
439
|
+
"defaultFrom": "desk"
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* One graphic-EQ fader's travel: EQ_GAIN_RANGE by reference (a graphic fader is an EQ gain), with the come-up value it needs as a row field — flat, 0 dB (graphic-eq-31 spec §3).
|
|
445
|
+
* @see data/kernels/geq.json
|
|
446
|
+
*/
|
|
447
|
+
export const GEQ_BAND_RANGE = {
|
|
448
|
+
"min": -15,
|
|
449
|
+
"max": 15,
|
|
450
|
+
"step": 0.5,
|
|
451
|
+
"unit": "dB",
|
|
452
|
+
"default": 0,
|
|
453
|
+
"defaultFrom": "desk"
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* The graphic EQ's band Q: the ISO third-octave bandwidth, `2^(1/6)/(2^(1/3) − 1) ≈ 4.318`, to one decimal as the M32 GEQ carries it. Read by the `geq31` preset and the graphic EQ's section design (graphic-eq-31 spec §3).
|
|
458
|
+
* @see data/kernels/geq.json
|
|
459
|
+
*/
|
|
460
|
+
export const GEQ_BAND_Q = 4.3;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* The prototype gain the `true` solve's first interaction matrix is evaluated at (spec §2, §3): the middle of the travel's magnitude, where a matched-Z section's dB response per dB of gain is closest to its average.
|
|
464
|
+
* @see data/kernels/geq.json
|
|
465
|
+
*/
|
|
466
|
+
export const GEQ_PROTO_DB = 12;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* The ceiling on a section gain the `true` solve may ask for (spec §3): above the travel's worst case (26.0 dB, the alternating ±15 set) with margin. A solve past it clamps and the row says so (`designClamped`), never silently.
|
|
470
|
+
* @see data/kernels/geq.json
|
|
471
|
+
*/
|
|
472
|
+
export const GEQ_DESIGN_GAIN_MAX_DB = 30;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* The precision limiter's travels (`2026-09-27-precision-limiter.md` §2), rendered into `omx_contract_limits.h` as `OMX_LIMITER_*` by `harness/contract-limits-gen.mjs`, so `mix_limiter.h` reads them and never restates one (R-094). The ceiling is a TRUE peak in dBFS (the ×4 detector); the look-ahead sets the declared latency; the release is the envelope's time constant on the gain depth.
|
|
476
|
+
* @see data/kernels/limiter.json
|
|
477
|
+
*/
|
|
478
|
+
export const LIMITER_LIMITS = {
|
|
479
|
+
"ceilingDb": {
|
|
480
|
+
"min": -12,
|
|
481
|
+
"max": 0,
|
|
482
|
+
"step": 0.1,
|
|
483
|
+
"unit": "dBFS",
|
|
484
|
+
"default": -1,
|
|
485
|
+
"defaultFrom": "desk"
|
|
486
|
+
},
|
|
487
|
+
"lookaheadMs": {
|
|
488
|
+
"min": 0.5,
|
|
489
|
+
"max": 5,
|
|
490
|
+
"step": 0.1,
|
|
491
|
+
"unit": "ms",
|
|
492
|
+
"default": 1.5,
|
|
493
|
+
"defaultFrom": "desk"
|
|
494
|
+
},
|
|
495
|
+
"releaseMs": {
|
|
496
|
+
"min": 1,
|
|
497
|
+
"max": 1000,
|
|
498
|
+
"step": 1,
|
|
499
|
+
"unit": "ms",
|
|
500
|
+
"default": 50,
|
|
501
|
+
"defaultFrom": "desk"
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* The phaser's sweep speed (§3). `omx_lfo_inc` freezes at Nyquist; this travel stays far below.
|
|
507
|
+
* @see data/kernels/phaser.json
|
|
508
|
+
*/
|
|
509
|
+
export const PHASER_RATE_RANGE = {
|
|
510
|
+
"min": 0.05,
|
|
511
|
+
"max": 5,
|
|
512
|
+
"step": 0.01,
|
|
513
|
+
"unit": "Hz",
|
|
514
|
+
"default": 0.5,
|
|
515
|
+
"defaultFrom": "desk"
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* The phaser's lowest centre frequency; the sweep runs upward from it (§3).
|
|
520
|
+
* @see data/kernels/phaser.json
|
|
521
|
+
*/
|
|
522
|
+
export const PHASER_BASE_RANGE = {
|
|
523
|
+
"min": 50,
|
|
524
|
+
"max": 2000,
|
|
525
|
+
"step": 1,
|
|
526
|
+
"unit": "Hz",
|
|
527
|
+
"default": 200,
|
|
528
|
+
"defaultFrom": "desk"
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* The sweep's span above `base`, in octaves; 0 is a static phaser at `base` (§3).
|
|
533
|
+
* @see data/kernels/phaser.json
|
|
534
|
+
*/
|
|
535
|
+
export const PHASER_DEPTH_RANGE = {
|
|
536
|
+
"min": 0,
|
|
537
|
+
"max": 6,
|
|
538
|
+
"step": 0.1,
|
|
539
|
+
"unit": "oct",
|
|
540
|
+
"default": 4,
|
|
541
|
+
"defaultFrom": "desk"
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* The phaser's section count per leg — EVEN, 2 … 12 (§2: two sections per notch). Its ends are the kernel's state capacity (`OMX_PHASER_MAX_STAGES` reads `OMX_PHASER_STAGES_RANGE_MAX`); the row moves it through the closed member set PHASER_STAGE_COUNTS, which is derived from it.
|
|
546
|
+
* @see data/kernels/phaser.json
|
|
547
|
+
*/
|
|
548
|
+
export const PHASER_STAGES_RANGE = {
|
|
549
|
+
"min": 2,
|
|
550
|
+
"max": 12,
|
|
551
|
+
"step": 2,
|
|
552
|
+
"unit": "",
|
|
553
|
+
"default": 6,
|
|
554
|
+
"defaultFrom": "desk"
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* The member set the `stages` field accepts: PHASER_STAGES_RANGE walked by its step.
|
|
559
|
+
* @see data/kernels/phaser.json
|
|
560
|
+
*/
|
|
561
|
+
export const PHASER_STAGE_COUNTS = [
|
|
562
|
+
2,
|
|
563
|
+
4,
|
|
564
|
+
6,
|
|
565
|
+
8,
|
|
566
|
+
10,
|
|
567
|
+
12
|
|
568
|
+
];
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* The phaser's SIGNED resonance (§3). ±0.9, tighter than the flanger's ±0.95: the loop runs through a time-varying chain. Declared here (the refusal half); the kernel's clamp is the enforcement half.
|
|
572
|
+
* @see data/kernels/phaser.json
|
|
573
|
+
*/
|
|
574
|
+
export const PHASER_FEEDBACK_RANGE = {
|
|
575
|
+
"min": -0.9,
|
|
576
|
+
"max": 0.9,
|
|
577
|
+
"step": 0.01,
|
|
578
|
+
"unit": "",
|
|
579
|
+
"default": 0.4,
|
|
580
|
+
"defaultFrom": "desk"
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* The phaser's wet/dry, as an instance of DRIVE_MIX_RANGE: 50 %, where the notch is complete (§3).
|
|
585
|
+
* @see data/kernels/phaser.json
|
|
586
|
+
*/
|
|
587
|
+
export const PHASER_MIX_RANGE = {
|
|
588
|
+
"min": 0,
|
|
589
|
+
"max": 100,
|
|
590
|
+
"step": 0.1,
|
|
591
|
+
"unit": "%",
|
|
592
|
+
"default": 50,
|
|
593
|
+
"defaultFrom": "desk"
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* The phaser's continuous travels by row field — the ONE map its control bounds and its row travels both read (§3). `stages` is not here: it moves through PHASER_STAGE_COUNTS.
|
|
598
|
+
* @see data/kernels/phaser.json
|
|
599
|
+
*/
|
|
600
|
+
export const PHASER_TRAVELS = {
|
|
601
|
+
"rateHz": {
|
|
602
|
+
"min": 0.05,
|
|
603
|
+
"max": 5,
|
|
604
|
+
"step": 0.01,
|
|
605
|
+
"unit": "Hz",
|
|
606
|
+
"default": 0.5,
|
|
607
|
+
"defaultFrom": "desk"
|
|
608
|
+
},
|
|
609
|
+
"baseHz": {
|
|
610
|
+
"min": 50,
|
|
611
|
+
"max": 2000,
|
|
612
|
+
"step": 1,
|
|
613
|
+
"unit": "Hz",
|
|
614
|
+
"default": 200,
|
|
615
|
+
"defaultFrom": "desk"
|
|
616
|
+
},
|
|
617
|
+
"depthOct": {
|
|
618
|
+
"min": 0,
|
|
619
|
+
"max": 6,
|
|
620
|
+
"step": 0.1,
|
|
621
|
+
"unit": "oct",
|
|
622
|
+
"default": 4,
|
|
623
|
+
"defaultFrom": "desk"
|
|
624
|
+
},
|
|
625
|
+
"feedback": {
|
|
626
|
+
"min": -0.9,
|
|
627
|
+
"max": 0.9,
|
|
628
|
+
"step": 0.01,
|
|
629
|
+
"unit": "",
|
|
630
|
+
"default": 0.4,
|
|
631
|
+
"defaultFrom": "desk"
|
|
632
|
+
},
|
|
633
|
+
"mix": {
|
|
634
|
+
"min": 0,
|
|
635
|
+
"max": 100,
|
|
636
|
+
"step": 0.1,
|
|
637
|
+
"unit": "%",
|
|
638
|
+
"default": 50,
|
|
639
|
+
"defaultFrom": "desk"
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* PITCH_LIMITS, moved from openmixer.
|
|
645
|
+
* @see data/kernels/pitch.json
|
|
646
|
+
*/
|
|
647
|
+
export const PITCH_LIMITS = {
|
|
648
|
+
"semitones": {
|
|
649
|
+
"min": -12,
|
|
650
|
+
"max": 12,
|
|
651
|
+
"default": 0
|
|
652
|
+
},
|
|
653
|
+
"cents": {
|
|
654
|
+
"min": -50,
|
|
655
|
+
"max": 50,
|
|
656
|
+
"default": 0
|
|
657
|
+
},
|
|
658
|
+
"mix": {
|
|
659
|
+
"min": 0,
|
|
660
|
+
"max": 100,
|
|
661
|
+
"default": 100
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* PITCH_KERNEL, moved from openmixer.
|
|
667
|
+
* @see data/kernels/pitch.json
|
|
668
|
+
*/
|
|
669
|
+
export const PITCH_KERNEL = {
|
|
670
|
+
"windowMs": 40,
|
|
671
|
+
"prefilterCeilingHz": 20000,
|
|
672
|
+
"prefilterQ": 0.7071067811865476
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* PLATE's tank MODULATION DEPTH, in percent of Dattorro's own excursion (±8 samples at his 29 761 Hz tuning rate, scaled to the live rate the way the tank lengths are). 100 is the depth the plate has always run at, so the come-up is bit-identical to the plate before the field; 0 brings back the unmodulated tank's ring; 400 is the wide, chorused EMT-style swing. The rate stays 1 Hz. Only PLATE reads it (`2026-09-26-native-fx-catalogue.md` §2, Reverb).
|
|
677
|
+
* @see data/kernels/reverb.json
|
|
678
|
+
*/
|
|
679
|
+
export const REVERB_PLATE_MOD_DEPTH_RANGE = {
|
|
680
|
+
"min": 0,
|
|
681
|
+
"max": 400,
|
|
682
|
+
"step": 1,
|
|
683
|
+
"unit": "%",
|
|
684
|
+
"default": 100,
|
|
685
|
+
"defaultFrom": "desk"
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* The horn's chorale (slow) rate, Hz.
|
|
690
|
+
* @see data/kernels/rotary.json
|
|
691
|
+
*/
|
|
692
|
+
export const ROTARY_HORN_SLOW_RANGE = {
|
|
693
|
+
"min": 0.1,
|
|
694
|
+
"max": 2,
|
|
695
|
+
"step": 0.01,
|
|
696
|
+
"unit": "Hz",
|
|
697
|
+
"default": 0.8,
|
|
698
|
+
"defaultFrom": "desk"
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* The horn's tremolo (fast) rate, Hz.
|
|
703
|
+
* @see data/kernels/rotary.json
|
|
704
|
+
*/
|
|
705
|
+
export const ROTARY_HORN_FAST_RANGE = {
|
|
706
|
+
"min": 3,
|
|
707
|
+
"max": 10,
|
|
708
|
+
"step": 0.01,
|
|
709
|
+
"unit": "Hz",
|
|
710
|
+
"default": 6.8,
|
|
711
|
+
"defaultFrom": "desk"
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* The drum's chorale (slow) rate, Hz.
|
|
716
|
+
* @see data/kernels/rotary.json
|
|
717
|
+
*/
|
|
718
|
+
export const ROTARY_DRUM_SLOW_RANGE = {
|
|
719
|
+
"min": 0.1,
|
|
720
|
+
"max": 2,
|
|
721
|
+
"step": 0.01,
|
|
722
|
+
"unit": "Hz",
|
|
723
|
+
"default": 0.7,
|
|
724
|
+
"defaultFrom": "desk"
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* The drum's tremolo (fast) rate, Hz.
|
|
729
|
+
* @see data/kernels/rotary.json
|
|
730
|
+
*/
|
|
731
|
+
export const ROTARY_DRUM_FAST_RANGE = {
|
|
732
|
+
"min": 3,
|
|
733
|
+
"max": 10,
|
|
734
|
+
"step": 0.01,
|
|
735
|
+
"unit": "Hz",
|
|
736
|
+
"default": 5.9,
|
|
737
|
+
"defaultFrom": "desk"
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* The scale on every speed-change time constant (`ROTARY_*_ACCEL_MS`/`_DECEL_MS`): 2 is twice as slow.
|
|
742
|
+
* @see data/kernels/rotary.json
|
|
743
|
+
*/
|
|
744
|
+
export const ROTARY_ACCEL_RANGE = {
|
|
745
|
+
"min": 0.25,
|
|
746
|
+
"max": 4,
|
|
747
|
+
"step": 0.01,
|
|
748
|
+
"unit": "x",
|
|
749
|
+
"default": 1,
|
|
750
|
+
"defaultFrom": "desk"
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* The drum/horn balance, percent: −100 drum only, +100 horn only; the law attenuates, never boosts.
|
|
755
|
+
* @see data/kernels/rotary.json
|
|
756
|
+
*/
|
|
757
|
+
export const ROTARY_BALANCE_RANGE = {
|
|
758
|
+
"min": -100,
|
|
759
|
+
"max": 100,
|
|
760
|
+
"step": 1,
|
|
761
|
+
"unit": "%",
|
|
762
|
+
"default": 0,
|
|
763
|
+
"defaultFrom": "desk"
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* The rotary's wet/dry, as an instance of DRIVE_MIX_RANGE: 100 %, because a rotary on an insert IS the sound (it replaces the cabinet) and an `fxReturn` wants it wet too (§3).
|
|
768
|
+
* @see data/kernels/rotary.json
|
|
769
|
+
*/
|
|
770
|
+
export const ROTARY_MIX_RANGE = {
|
|
771
|
+
"min": 0,
|
|
772
|
+
"max": 100,
|
|
773
|
+
"step": 0.1,
|
|
774
|
+
"unit": "%",
|
|
775
|
+
"default": 100,
|
|
776
|
+
"defaultFrom": "desk"
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* The rotary's numeric travels, field → declaration: the ONE map the row's state, its bounds, its OPTIONS and its controller read (`speed` is a member set, ROTARY_SPEEDS, beside it).
|
|
781
|
+
* @see data/kernels/rotary.json
|
|
782
|
+
*/
|
|
783
|
+
export const ROTARY_TRAVELS = {
|
|
784
|
+
"hornSlowHz": {
|
|
785
|
+
"min": 0.1,
|
|
786
|
+
"max": 2,
|
|
787
|
+
"step": 0.01,
|
|
788
|
+
"unit": "Hz",
|
|
789
|
+
"default": 0.8,
|
|
790
|
+
"defaultFrom": "desk"
|
|
791
|
+
},
|
|
792
|
+
"hornFastHz": {
|
|
793
|
+
"min": 3,
|
|
794
|
+
"max": 10,
|
|
795
|
+
"step": 0.01,
|
|
796
|
+
"unit": "Hz",
|
|
797
|
+
"default": 6.8,
|
|
798
|
+
"defaultFrom": "desk"
|
|
799
|
+
},
|
|
800
|
+
"drumSlowHz": {
|
|
801
|
+
"min": 0.1,
|
|
802
|
+
"max": 2,
|
|
803
|
+
"step": 0.01,
|
|
804
|
+
"unit": "Hz",
|
|
805
|
+
"default": 0.7,
|
|
806
|
+
"defaultFrom": "desk"
|
|
807
|
+
},
|
|
808
|
+
"drumFastHz": {
|
|
809
|
+
"min": 3,
|
|
810
|
+
"max": 10,
|
|
811
|
+
"step": 0.01,
|
|
812
|
+
"unit": "Hz",
|
|
813
|
+
"default": 5.9,
|
|
814
|
+
"defaultFrom": "desk"
|
|
815
|
+
},
|
|
816
|
+
"accel": {
|
|
817
|
+
"min": 0.25,
|
|
818
|
+
"max": 4,
|
|
819
|
+
"step": 0.01,
|
|
820
|
+
"unit": "x",
|
|
821
|
+
"default": 1,
|
|
822
|
+
"defaultFrom": "desk"
|
|
823
|
+
},
|
|
824
|
+
"balance": {
|
|
825
|
+
"min": -100,
|
|
826
|
+
"max": 100,
|
|
827
|
+
"step": 1,
|
|
828
|
+
"unit": "%",
|
|
829
|
+
"default": 0,
|
|
830
|
+
"defaultFrom": "desk"
|
|
831
|
+
},
|
|
832
|
+
"mix": {
|
|
833
|
+
"min": 0,
|
|
834
|
+
"max": 100,
|
|
835
|
+
"step": 0.1,
|
|
836
|
+
"unit": "%",
|
|
837
|
+
"default": 100,
|
|
838
|
+
"defaultFrom": "desk"
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* The complementary split's low-pass corner, Hz: the Leslie 122's passive crossover.
|
|
844
|
+
* @see data/kernels/rotary.json
|
|
845
|
+
*/
|
|
846
|
+
export const ROTARY_CROSSOVER_HZ = 800;
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* The shortest Doppler read, ms — above the order-3 read's one-sample reach at every rate.
|
|
850
|
+
* @see data/kernels/rotary.json
|
|
851
|
+
*/
|
|
852
|
+
export const ROTARY_BASE_MS = 0.1;
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* The horn's Doppler excursion `D_horn`, ms: a 0.15 m radius over 343 m/s.
|
|
856
|
+
* @see data/kernels/rotary.json
|
|
857
|
+
*/
|
|
858
|
+
export const ROTARY_HORN_DOPPLER_MS = 0.44;
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* The drum's Doppler excursion `D_drum`, ms: its baffle turns over a fixed woofer.
|
|
862
|
+
* @see data/kernels/rotary.json
|
|
863
|
+
*/
|
|
864
|
+
export const ROTARY_DRUM_DOPPLER_MS = 0.29;
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* The horn's amplitude swing `m_horn` (a −6 dB directivity dip), a ratio.
|
|
868
|
+
* @see data/kernels/rotary.json
|
|
869
|
+
*/
|
|
870
|
+
export const ROTARY_HORN_AM = 0.5;
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* The drum baffle's amplitude swing `m_drum`, a ratio.
|
|
874
|
+
* @see data/kernels/rotary.json
|
|
875
|
+
*/
|
|
876
|
+
export const ROTARY_DRUM_AM = 0.3;
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* The horn rotor's speed-up time constant, ms (at `accel` 1).
|
|
880
|
+
* @see data/kernels/rotary.json
|
|
881
|
+
*/
|
|
882
|
+
export const ROTARY_HORN_ACCEL_MS = 330;
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* The horn rotor's slow-down time constant, ms (at `accel` 1).
|
|
886
|
+
* @see data/kernels/rotary.json
|
|
887
|
+
*/
|
|
888
|
+
export const ROTARY_HORN_DECEL_MS = 500;
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* The drum rotor's speed-up time constant, ms (at `accel` 1).
|
|
892
|
+
* @see data/kernels/rotary.json
|
|
893
|
+
*/
|
|
894
|
+
export const ROTARY_DRUM_ACCEL_MS = 1700;
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* The drum rotor's slow-down time constant, ms (at `accel` 1).
|
|
898
|
+
* @see data/kernels/rotary.json
|
|
899
|
+
*/
|
|
900
|
+
export const ROTARY_DRUM_DECEL_MS = 1800;
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Below this rate, Hz, a rotor coasting toward `stop` is snapped to exactly 0.
|
|
904
|
+
* @see data/kernels/rotary.json
|
|
905
|
+
*/
|
|
906
|
+
export const ROTARY_STOP_EPS = 0.0001;
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* One inline ring per rotor per leg, floats — at least the 192 kHz need of the horn's sweep.
|
|
910
|
+
* @see data/kernels/rotary.json
|
|
911
|
+
*/
|
|
912
|
+
export const ROTARY_RING_FLOATS = 256;
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* The legal range of each transient-designer field (engineering units), transient spec §3. Every kind comes up flat: no per-kind default differs. `harness/contract-limits-gen.mjs` emits each as `OMX_TRANSIENT_<FIELD>_{MIN,MAX,DEFAULT}` for `mix_transient.h`'s resolve; the row publishes them on OPTIONS when it lands (issue #921).
|
|
916
|
+
* @see data/kernels/transient.json
|
|
917
|
+
*/
|
|
918
|
+
export const TRANSIENT_LIMITS = {
|
|
919
|
+
"attackDb": {
|
|
920
|
+
"min": -24,
|
|
921
|
+
"max": 24,
|
|
922
|
+
"step": 0.1,
|
|
923
|
+
"unit": "dB",
|
|
924
|
+
"default": 0,
|
|
925
|
+
"defaultFrom": "desk"
|
|
926
|
+
},
|
|
927
|
+
"sustainDb": {
|
|
928
|
+
"min": -24,
|
|
929
|
+
"max": 24,
|
|
930
|
+
"step": 0.1,
|
|
931
|
+
"unit": "dB",
|
|
932
|
+
"default": 0,
|
|
933
|
+
"defaultFrom": "desk"
|
|
934
|
+
},
|
|
935
|
+
"attackTimeMs": {
|
|
936
|
+
"min": 2,
|
|
937
|
+
"max": 50,
|
|
938
|
+
"step": 0.1,
|
|
939
|
+
"unit": "ms",
|
|
940
|
+
"default": 10,
|
|
941
|
+
"defaultFrom": "desk"
|
|
942
|
+
},
|
|
943
|
+
"sustainTimeMs": {
|
|
944
|
+
"min": 50,
|
|
945
|
+
"max": 2000,
|
|
946
|
+
"step": 1,
|
|
947
|
+
"unit": "ms",
|
|
948
|
+
"default": 250,
|
|
949
|
+
"defaultFrom": "desk"
|
|
950
|
+
},
|
|
951
|
+
"outputDb": {
|
|
952
|
+
"min": -24,
|
|
953
|
+
"max": 12,
|
|
954
|
+
"step": 0.1,
|
|
955
|
+
"unit": "dB",
|
|
956
|
+
"default": 0,
|
|
957
|
+
"defaultFrom": "desk"
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* The fast follower's attack, ms: exactly the dynamics detector's no-oversampling line (transient spec §3, L7).
|
|
963
|
+
* @see data/kernels/transient.json
|
|
964
|
+
*/
|
|
965
|
+
export const TRANSIENT_FAST_ATTACK_MS = 0.5;
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* The fast follower's release, ms: one 50 Hz cycle held, a 180 bpm sixteenth forgotten (transient spec §3).
|
|
969
|
+
* @see data/kernels/transient.json
|
|
970
|
+
*/
|
|
971
|
+
export const TRANSIENT_FAST_RELEASE_MS = 20;
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* The contrast, dB, at which a knob reaches its full value (transient spec §2, §3).
|
|
975
|
+
* @see data/kernels/transient.json
|
|
976
|
+
*/
|
|
977
|
+
export const TRANSIENT_REF_DB = 6;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* The detector floor, linear (−100 dBFS): below it both envelopes read the floor and the stage is unity (transient spec §3).
|
|
981
|
+
* @see data/kernels/transient.json
|
|
982
|
+
*/
|
|
983
|
+
export const TRANSIENT_FLOOR_LIN = 0.00001;
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* The tremolo's LFO rate (the catalogue row's 0.1..20 Hz); 4 Hz is the classic amp tremolo.
|
|
987
|
+
* @see data/kernels/tremolo.json
|
|
988
|
+
*/
|
|
989
|
+
export const TREMOLO_RATE_RANGE = {
|
|
990
|
+
"min": 0.1,
|
|
991
|
+
"max": 20,
|
|
992
|
+
"step": 0.01,
|
|
993
|
+
"unit": "Hz",
|
|
994
|
+
"default": 4,
|
|
995
|
+
"defaultFrom": "desk"
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* The tremolo's modulation depth: the gain's trough is `1 − mix·depth`.
|
|
1000
|
+
* @see data/kernels/tremolo.json
|
|
1001
|
+
*/
|
|
1002
|
+
export const TREMOLO_DEPTH_RANGE = {
|
|
1003
|
+
"min": 0,
|
|
1004
|
+
"max": 100,
|
|
1005
|
+
"step": 0.1,
|
|
1006
|
+
"unit": "%",
|
|
1007
|
+
"default": 50,
|
|
1008
|
+
"defaultFrom": "desk"
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* The tremolo's wet/dry, as an instance of DRIVE_MIX_RANGE: 100 %. The kernel folds the wet/dry into its one gain, so a strip tremolo IS the sound (as the rotary's mix 100 % is).
|
|
1013
|
+
* @see data/kernels/tremolo.json
|
|
1014
|
+
*/
|
|
1015
|
+
export const TREMOLO_MIX_RANGE = {
|
|
1016
|
+
"min": 0,
|
|
1017
|
+
"max": 100,
|
|
1018
|
+
"step": 0.1,
|
|
1019
|
+
"unit": "%",
|
|
1020
|
+
"default": 100,
|
|
1021
|
+
"defaultFrom": "desk"
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* The tremolo's numeric travels, field → declaration: the ONE map the row's state, its bounds, its OPTIONS and its controller read (`mode` is a member set, TREMOLO_MODES, beside it).
|
|
1026
|
+
* @see data/kernels/tremolo.json
|
|
1027
|
+
*/
|
|
1028
|
+
export const TREMOLO_TRAVELS = {
|
|
1029
|
+
"rateHz": {
|
|
1030
|
+
"min": 0.1,
|
|
1031
|
+
"max": 20,
|
|
1032
|
+
"step": 0.01,
|
|
1033
|
+
"unit": "Hz",
|
|
1034
|
+
"default": 4,
|
|
1035
|
+
"defaultFrom": "desk"
|
|
1036
|
+
},
|
|
1037
|
+
"depth": {
|
|
1038
|
+
"min": 0,
|
|
1039
|
+
"max": 100,
|
|
1040
|
+
"step": 0.1,
|
|
1041
|
+
"unit": "%",
|
|
1042
|
+
"default": 50,
|
|
1043
|
+
"defaultFrom": "desk"
|
|
1044
|
+
},
|
|
1045
|
+
"mix": {
|
|
1046
|
+
"min": 0,
|
|
1047
|
+
"max": 100,
|
|
1048
|
+
"step": 0.1,
|
|
1049
|
+
"unit": "%",
|
|
1050
|
+
"default": 100,
|
|
1051
|
+
"defaultFrom": "desk"
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* The Q of a second-order Butterworth section, 1/√2 — maximally flat, the one value every Butterworth biquad in the tree designs at: the LR4 crossover section, the pitch pre-filter and the drive's tilt / band / HF sections, in TS and (as `OMX_BUTTERWORTH_Q`) in C (§7, the scalar door).
|
|
1057
|
+
* @see data/primitives.json
|
|
1058
|
+
*/
|
|
1059
|
+
export const BUTTERWORTH_Q = 0.7071067811865476;
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* ALLPASS_LIMITS, moved from openmixer.
|
|
1063
|
+
* @see data/primitives.json
|
|
1064
|
+
*/
|
|
1065
|
+
export const ALLPASS_LIMITS = {
|
|
1066
|
+
"unityTolerance": 0.0001
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* XOVER_LIMITS, moved from openmixer.
|
|
1071
|
+
* @see data/primitives.json
|
|
1072
|
+
*/
|
|
1073
|
+
export const XOVER_LIMITS = {
|
|
1074
|
+
"lr2SectionQ": 0.5,
|
|
1075
|
+
"lr4SectionQ": 0.7071067811865476,
|
|
1076
|
+
"partitionTolerance": 0.00001
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* The order the chorus and flanger construct their lines with (four taps; the fdelay spec §1).
|
|
1081
|
+
* @see data/primitives.json
|
|
1082
|
+
*/
|
|
1083
|
+
export const FDELAY_MOD_READ_ORDER = 3;
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* `Λ` for the chorus/flanger read — ONE value; the header and every law read this.
|
|
1087
|
+
* @see data/primitives.json
|
|
1088
|
+
*/
|
|
1089
|
+
export const FDELAY_READ_L1_NORM = 1.25;
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* The standard sample rates (Hz) the desk recognises — the pro-audio superset. One source of truth for the clock controller's `clock.allowed-rates` seed, the force row's validation ceiling and the device-rate probe fallback. What the operator is OFFERED is always the DEVICE's own `supportedRates` (`2026-07-15-clock-rate-honest-force-rate.md`) — this is the ceiling a write is refused by, never a menu.
|
|
1093
|
+
* @see data/rates.json
|
|
1094
|
+
*/
|
|
1095
|
+
export const STANDARD_SAMPLE_RATES = [
|
|
1096
|
+
44100,
|
|
1097
|
+
48000,
|
|
1098
|
+
88200,
|
|
1099
|
+
96000,
|
|
1100
|
+
176400,
|
|
1101
|
+
192000
|
|
1102
|
+
];
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* THE declaration: 192 kHz at quantum 128.
|
|
1106
|
+
* @see data/rates.json
|
|
1107
|
+
*/
|
|
1108
|
+
export const RT_HARD_TARGET = {
|
|
1109
|
+
"rate": 192000,
|
|
1110
|
+
"quantum": 128
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* The rate a unit-range DSP knob is quoted at (R-058): the rate this desk runs (ruling 2026-09-14). A kernel raises the knob to `REF / sr`, so at this rate the exponent is exactly 1 and the response is bit-for-bit the one cut before the law. Emitted to C as `OMX_DSP_KNOB_REFERENCE_RATE`; the reverb damping and FX-delay tone references `#define` from it.
|
|
1115
|
+
* @see data/rates.json
|
|
1116
|
+
*/
|
|
1117
|
+
export const DSP_KNOB_REFERENCE_RATE = 96000;
|