@openmixer/omx-contract 1.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.
@@ -117,6 +117,42 @@
117
117
  #define OMX_CHORUS_SPREAD_RANGE_MIN 0
118
118
  #define OMX_CHORUS_SPREAD_RANGE_STEP 0.01f
119
119
  #define OMX_CHORUS_SPREAD_RANGE_STEP_DOUBLE 0.01
120
+ #define OMX_DEESS_ATTACK_RANGE_DEFAULT 1
121
+ #define OMX_DEESS_ATTACK_RANGE_MAX 50
122
+ #define OMX_DEESS_ATTACK_RANGE_MIN 0.1f
123
+ #define OMX_DEESS_ATTACK_RANGE_MIN_DOUBLE 0.1
124
+ #define OMX_DEESS_ATTACK_RANGE_STEP 0.1f
125
+ #define OMX_DEESS_ATTACK_RANGE_STEP_DOUBLE 0.1
126
+ #define OMX_DEESS_FREQ_RANGE_DEFAULT 7000
127
+ #define OMX_DEESS_FREQ_RANGE_MAX 16000
128
+ #define OMX_DEESS_FREQ_RANGE_MIN 2000
129
+ #define OMX_DEESS_FREQ_RANGE_STEP 10
130
+ #define OMX_DEESS_KNEE_DB 6
131
+ #define OMX_DEESS_RANGE_RANGE_DEFAULT -12
132
+ #define OMX_DEESS_RANGE_RANGE_MAX 0
133
+ #define OMX_DEESS_RANGE_RANGE_MIN -24
134
+ #define OMX_DEESS_RANGE_RANGE_STEP 0.5f
135
+ #define OMX_DEESS_RANGE_RANGE_STEP_DOUBLE 0.5
136
+ #define OMX_DEESS_RATIO_RANGE_DEFAULT 4
137
+ #define OMX_DEESS_RATIO_RANGE_MAX 20
138
+ #define OMX_DEESS_RATIO_RANGE_MIN 1
139
+ #define OMX_DEESS_RATIO_RANGE_STEP 0.1f
140
+ #define OMX_DEESS_RATIO_RANGE_STEP_DOUBLE 0.1
141
+ #define OMX_DEESS_RELEASE_RANGE_DEFAULT 60
142
+ #define OMX_DEESS_RELEASE_RANGE_MAX 500
143
+ #define OMX_DEESS_RELEASE_RANGE_MIN 5
144
+ #define OMX_DEESS_RELEASE_RANGE_STEP 1
145
+ #define OMX_DEESS_THRESHOLD_RANGE_DEFAULT -30
146
+ #define OMX_DEESS_THRESHOLD_RANGE_MAX 0
147
+ #define OMX_DEESS_THRESHOLD_RANGE_MIN -60
148
+ #define OMX_DEESS_THRESHOLD_RANGE_STEP 0.5f
149
+ #define OMX_DEESS_THRESHOLD_RANGE_STEP_DOUBLE 0.5
150
+ #define OMX_DEESS_WIDTH_RANGE_DEFAULT 1
151
+ #define OMX_DEESS_WIDTH_RANGE_MAX 4
152
+ #define OMX_DEESS_WIDTH_RANGE_MIN 0.25f
153
+ #define OMX_DEESS_WIDTH_RANGE_MIN_DOUBLE 0.25
154
+ #define OMX_DEESS_WIDTH_RANGE_STEP 0.05f
155
+ #define OMX_DEESS_WIDTH_RANGE_STEP_DOUBLE 0.05
120
156
  #define OMX_DELAY_MIX_RANGE_BY_KIND_FX_RETURN 1
121
157
  #define OMX_DELAY_MIX_RANGE_DEFAULT 0.3f
122
158
  #define OMX_DELAY_MIX_RANGE_DEFAULT_DOUBLE 0.3
@@ -135,6 +171,31 @@
135
171
  #define OMX_FDELAY_MOD_READ_ORDER 3
136
172
  #define OMX_FDELAY_READ_L1_NORM 1.25f
137
173
  #define OMX_FDELAY_READ_L1_NORM_DOUBLE 1.25
174
+ #define OMX_FLANGER_DEPTH_RANGE_DEFAULT 2
175
+ #define OMX_FLANGER_DEPTH_RANGE_MAX 5
176
+ #define OMX_FLANGER_DEPTH_RANGE_MIN 0
177
+ #define OMX_FLANGER_DEPTH_RANGE_STEP 0.1f
178
+ #define OMX_FLANGER_DEPTH_RANGE_STEP_DOUBLE 0.1
179
+ #define OMX_FLANGER_FEEDBACK_RANGE_DEFAULT 0.6f
180
+ #define OMX_FLANGER_FEEDBACK_RANGE_DEFAULT_DOUBLE 0.6
181
+ #define OMX_FLANGER_FEEDBACK_RANGE_MAX 0.95f
182
+ #define OMX_FLANGER_FEEDBACK_RANGE_MAX_DOUBLE 0.95
183
+ #define OMX_FLANGER_FEEDBACK_RANGE_MIN -0.95f
184
+ #define OMX_FLANGER_FEEDBACK_RANGE_MIN_DOUBLE -0.95
185
+ #define OMX_FLANGER_FEEDBACK_RANGE_STEP 0.01f
186
+ #define OMX_FLANGER_FEEDBACK_RANGE_STEP_DOUBLE 0.01
187
+ #define OMX_FLANGER_MIX_RANGE_DEFAULT 50
188
+ #define OMX_FLANGER_MIX_RANGE_MAX 100
189
+ #define OMX_FLANGER_MIX_RANGE_MIN 0
190
+ #define OMX_FLANGER_MIX_RANGE_STEP 0.1f
191
+ #define OMX_FLANGER_MIX_RANGE_STEP_DOUBLE 0.1
192
+ #define OMX_FLANGER_RATE_RANGE_DEFAULT 0.25f
193
+ #define OMX_FLANGER_RATE_RANGE_DEFAULT_DOUBLE 0.25
194
+ #define OMX_FLANGER_RATE_RANGE_MAX 5
195
+ #define OMX_FLANGER_RATE_RANGE_MIN 0.05f
196
+ #define OMX_FLANGER_RATE_RANGE_MIN_DOUBLE 0.05
197
+ #define OMX_FLANGER_RATE_RANGE_STEP 0.01f
198
+ #define OMX_FLANGER_RATE_RANGE_STEP_DOUBLE 0.01
138
199
  #define OMX_FX_DELAY_FEEDBACK_RANGE_DEFAULT 0.3f
139
200
  #define OMX_FX_DELAY_FEEDBACK_RANGE_DEFAULT_DOUBLE 0.3
140
201
  #define OMX_FX_DELAY_FEEDBACK_RANGE_MAX 0.99f
@@ -146,6 +207,48 @@
146
207
  #define OMX_FX_DELAY_TIME_RANGE_MIN 0
147
208
  #define OMX_FX_DELAY_TIME_RANGE_STEP 1
148
209
  #define OMX_GEQ_BANDS 31
210
+ #define OMX_GEQ_BAND_Q 4.3f
211
+ #define OMX_GEQ_BAND_Q_DOUBLE 4.3
212
+ #define OMX_GEQ_BAND_RANGE_DEFAULT 0
213
+ #define OMX_GEQ_BAND_RANGE_MAX 15
214
+ #define OMX_GEQ_BAND_RANGE_MIN -15
215
+ #define OMX_GEQ_BAND_RANGE_STEP 0.5f
216
+ #define OMX_GEQ_BAND_RANGE_STEP_DOUBLE 0.5
217
+ #define OMX_GEQ_DESIGN_GAIN_MAX_DB 30
218
+ #define OMX_GEQ_PROTO_DB 12
219
+ #define OMX_PHASER_BASE_RANGE_DEFAULT 200
220
+ #define OMX_PHASER_BASE_RANGE_MAX 2000
221
+ #define OMX_PHASER_BASE_RANGE_MIN 50
222
+ #define OMX_PHASER_BASE_RANGE_STEP 1
223
+ #define OMX_PHASER_DEPTH_RANGE_DEFAULT 4
224
+ #define OMX_PHASER_DEPTH_RANGE_MAX 6
225
+ #define OMX_PHASER_DEPTH_RANGE_MIN 0
226
+ #define OMX_PHASER_DEPTH_RANGE_STEP 0.1f
227
+ #define OMX_PHASER_DEPTH_RANGE_STEP_DOUBLE 0.1
228
+ #define OMX_PHASER_FEEDBACK_RANGE_DEFAULT 0.4f
229
+ #define OMX_PHASER_FEEDBACK_RANGE_DEFAULT_DOUBLE 0.4
230
+ #define OMX_PHASER_FEEDBACK_RANGE_MAX 0.9f
231
+ #define OMX_PHASER_FEEDBACK_RANGE_MAX_DOUBLE 0.9
232
+ #define OMX_PHASER_FEEDBACK_RANGE_MIN -0.9f
233
+ #define OMX_PHASER_FEEDBACK_RANGE_MIN_DOUBLE -0.9
234
+ #define OMX_PHASER_FEEDBACK_RANGE_STEP 0.01f
235
+ #define OMX_PHASER_FEEDBACK_RANGE_STEP_DOUBLE 0.01
236
+ #define OMX_PHASER_MIX_RANGE_DEFAULT 50
237
+ #define OMX_PHASER_MIX_RANGE_MAX 100
238
+ #define OMX_PHASER_MIX_RANGE_MIN 0
239
+ #define OMX_PHASER_MIX_RANGE_STEP 0.1f
240
+ #define OMX_PHASER_MIX_RANGE_STEP_DOUBLE 0.1
241
+ #define OMX_PHASER_RATE_RANGE_DEFAULT 0.5f
242
+ #define OMX_PHASER_RATE_RANGE_DEFAULT_DOUBLE 0.5
243
+ #define OMX_PHASER_RATE_RANGE_MAX 5
244
+ #define OMX_PHASER_RATE_RANGE_MIN 0.05f
245
+ #define OMX_PHASER_RATE_RANGE_MIN_DOUBLE 0.05
246
+ #define OMX_PHASER_RATE_RANGE_STEP 0.01f
247
+ #define OMX_PHASER_RATE_RANGE_STEP_DOUBLE 0.01
248
+ #define OMX_PHASER_STAGES_RANGE_DEFAULT 6
249
+ #define OMX_PHASER_STAGES_RANGE_MAX 12
250
+ #define OMX_PHASER_STAGES_RANGE_MIN 2
251
+ #define OMX_PHASER_STAGES_RANGE_STEP 2
149
252
  #define OMX_PITCH_PREFILTER_CEILING_HZ 20000
150
253
  #define OMX_PITCH_PREFILTER_Q 0.7071067811865476f
151
254
  #define OMX_PITCH_PREFILTER_Q_DOUBLE 0.7071067811865476
@@ -161,6 +264,65 @@
161
264
  #define OMX_REVERB_PLATE_MOD_DEPTH_RANGE_MAX 400
162
265
  #define OMX_REVERB_PLATE_MOD_DEPTH_RANGE_MIN 0
163
266
  #define OMX_REVERB_PLATE_MOD_DEPTH_RANGE_STEP 1
267
+ #define OMX_ROTARY_ACCEL_RANGE_DEFAULT 1
268
+ #define OMX_ROTARY_ACCEL_RANGE_MAX 4
269
+ #define OMX_ROTARY_ACCEL_RANGE_MIN 0.25f
270
+ #define OMX_ROTARY_ACCEL_RANGE_MIN_DOUBLE 0.25
271
+ #define OMX_ROTARY_ACCEL_RANGE_STEP 0.01f
272
+ #define OMX_ROTARY_ACCEL_RANGE_STEP_DOUBLE 0.01
273
+ #define OMX_ROTARY_BALANCE_RANGE_DEFAULT 0
274
+ #define OMX_ROTARY_BALANCE_RANGE_MAX 100
275
+ #define OMX_ROTARY_BALANCE_RANGE_MIN -100
276
+ #define OMX_ROTARY_BALANCE_RANGE_STEP 1
277
+ #define OMX_ROTARY_BASE_MS 0.1f
278
+ #define OMX_ROTARY_BASE_MS_DOUBLE 0.1
279
+ #define OMX_ROTARY_CROSSOVER_HZ 800
280
+ #define OMX_ROTARY_DRUM_ACCEL_MS 1700
281
+ #define OMX_ROTARY_DRUM_AM 0.3f
282
+ #define OMX_ROTARY_DRUM_AM_DOUBLE 0.3
283
+ #define OMX_ROTARY_DRUM_DECEL_MS 1800
284
+ #define OMX_ROTARY_DRUM_DOPPLER_MS 0.29f
285
+ #define OMX_ROTARY_DRUM_DOPPLER_MS_DOUBLE 0.29
286
+ #define OMX_ROTARY_DRUM_FAST_RANGE_DEFAULT 5.9f
287
+ #define OMX_ROTARY_DRUM_FAST_RANGE_DEFAULT_DOUBLE 5.9
288
+ #define OMX_ROTARY_DRUM_FAST_RANGE_MAX 10
289
+ #define OMX_ROTARY_DRUM_FAST_RANGE_MIN 3
290
+ #define OMX_ROTARY_DRUM_FAST_RANGE_STEP 0.01f
291
+ #define OMX_ROTARY_DRUM_FAST_RANGE_STEP_DOUBLE 0.01
292
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_DEFAULT 0.7f
293
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_DEFAULT_DOUBLE 0.7
294
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_MAX 2
295
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_MIN 0.1f
296
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_MIN_DOUBLE 0.1
297
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_STEP 0.01f
298
+ #define OMX_ROTARY_DRUM_SLOW_RANGE_STEP_DOUBLE 0.01
299
+ #define OMX_ROTARY_HORN_ACCEL_MS 330
300
+ #define OMX_ROTARY_HORN_AM 0.5f
301
+ #define OMX_ROTARY_HORN_AM_DOUBLE 0.5
302
+ #define OMX_ROTARY_HORN_DECEL_MS 500
303
+ #define OMX_ROTARY_HORN_DOPPLER_MS 0.44f
304
+ #define OMX_ROTARY_HORN_DOPPLER_MS_DOUBLE 0.44
305
+ #define OMX_ROTARY_HORN_FAST_RANGE_DEFAULT 6.8f
306
+ #define OMX_ROTARY_HORN_FAST_RANGE_DEFAULT_DOUBLE 6.8
307
+ #define OMX_ROTARY_HORN_FAST_RANGE_MAX 10
308
+ #define OMX_ROTARY_HORN_FAST_RANGE_MIN 3
309
+ #define OMX_ROTARY_HORN_FAST_RANGE_STEP 0.01f
310
+ #define OMX_ROTARY_HORN_FAST_RANGE_STEP_DOUBLE 0.01
311
+ #define OMX_ROTARY_HORN_SLOW_RANGE_DEFAULT 0.8f
312
+ #define OMX_ROTARY_HORN_SLOW_RANGE_DEFAULT_DOUBLE 0.8
313
+ #define OMX_ROTARY_HORN_SLOW_RANGE_MAX 2
314
+ #define OMX_ROTARY_HORN_SLOW_RANGE_MIN 0.1f
315
+ #define OMX_ROTARY_HORN_SLOW_RANGE_MIN_DOUBLE 0.1
316
+ #define OMX_ROTARY_HORN_SLOW_RANGE_STEP 0.01f
317
+ #define OMX_ROTARY_HORN_SLOW_RANGE_STEP_DOUBLE 0.01
318
+ #define OMX_ROTARY_MIX_RANGE_DEFAULT 100
319
+ #define OMX_ROTARY_MIX_RANGE_MAX 100
320
+ #define OMX_ROTARY_MIX_RANGE_MIN 0
321
+ #define OMX_ROTARY_MIX_RANGE_STEP 0.1f
322
+ #define OMX_ROTARY_MIX_RANGE_STEP_DOUBLE 0.1
323
+ #define OMX_ROTARY_RING_FLOATS 256
324
+ #define OMX_ROTARY_STOP_EPS 0.0001f
325
+ #define OMX_ROTARY_STOP_EPS_DOUBLE 0.0001
164
326
  #define OMX_RT_HARD_TARGET_QUANTUM 128
165
327
  #define OMX_RT_HARD_TARGET_RATE 192000
166
328
  #define OMX_TRANSIENT_FAST_ATTACK_MS 0.5f
@@ -169,6 +331,22 @@
169
331
  #define OMX_TRANSIENT_FLOOR_LIN 0.00001f
170
332
  #define OMX_TRANSIENT_FLOOR_LIN_DOUBLE 0.00001
171
333
  #define OMX_TRANSIENT_REF_DB 6
334
+ #define OMX_TREMOLO_DEPTH_RANGE_DEFAULT 50
335
+ #define OMX_TREMOLO_DEPTH_RANGE_MAX 100
336
+ #define OMX_TREMOLO_DEPTH_RANGE_MIN 0
337
+ #define OMX_TREMOLO_DEPTH_RANGE_STEP 0.1f
338
+ #define OMX_TREMOLO_DEPTH_RANGE_STEP_DOUBLE 0.1
339
+ #define OMX_TREMOLO_MIX_RANGE_DEFAULT 100
340
+ #define OMX_TREMOLO_MIX_RANGE_MAX 100
341
+ #define OMX_TREMOLO_MIX_RANGE_MIN 0
342
+ #define OMX_TREMOLO_MIX_RANGE_STEP 0.1f
343
+ #define OMX_TREMOLO_MIX_RANGE_STEP_DOUBLE 0.1
344
+ #define OMX_TREMOLO_RATE_RANGE_DEFAULT 4
345
+ #define OMX_TREMOLO_RATE_RANGE_MAX 20
346
+ #define OMX_TREMOLO_RATE_RANGE_MIN 0.1f
347
+ #define OMX_TREMOLO_RATE_RANGE_MIN_DOUBLE 0.1
348
+ #define OMX_TREMOLO_RATE_RANGE_STEP 0.01f
349
+ #define OMX_TREMOLO_RATE_RANGE_STEP_DOUBLE 0.01
172
350
  #define OMX_XOVER_LR2_SECTION_Q 0.5f
173
351
  #define OMX_XOVER_LR2_SECTION_Q_DOUBLE 0.5
174
352
  #define OMX_XOVER_LR4_SECTION_Q 0.7071067811865476f
@@ -177,6 +355,8 @@
177
355
  #define OMX_XOVER_PARTITION_TOLERANCE_DOUBLE 0.00001
178
356
 
179
357
  /* Every list rendered to C, sorted by list: OMX_<LIST>_COUNT and OMX_<LIST>_INIT { a, b, … }. */
358
+ #define OMX_PHASER_STAGE_COUNTS_COUNT 6u
359
+ #define OMX_PHASER_STAGE_COUNTS_INIT { 2, 4, 6, 8, 10, 12 }
180
360
  #define OMX_STANDARD_SAMPLE_RATES_COUNT 6u
181
361
  #define OMX_STANDARD_SAMPLE_RATES_INIT { 44100, 48000, 88200, 96000, 176400, 192000 }
182
362
 
package/lib/data.mjs CHANGED
@@ -50,7 +50,6 @@ export function rawValue(item) {
50
50
  case 'travels': return item.fields ?? item.travel;
51
51
  case 'sheet': return item.value;
52
52
  case 'list': return item.values;
53
- case 'plugin': return item.params;
54
53
  default: return undefined;
55
54
  }
56
55
  }
@@ -106,8 +105,7 @@ const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
106
105
 
107
106
  /**
108
107
  * Resolve every item: `{ name, rel, kind, doc, why?, unit?, c?, shape?, value }`, where `value` has
109
- * every `{ref}` and `{derive}` evaluated. A plugin's value is its parameters resolved against the
110
- * items they read.
108
+ * every `{ref}` and `{derive}` evaluated.
111
109
  * @throws naming the item: a reference that does not resolve, a cycle, an unknown derivation.
112
110
  */
113
111
  export function resolveData(data) {
@@ -145,32 +143,11 @@ export function resolveData(data) {
145
143
  if (item.unit !== undefined) r.unit = item.unit;
146
144
  if (item.c !== undefined) r.c = item.c;
147
145
  if (item.kind === 'travels') r.shape = item.fields ? 'table' : 'travel';
148
- if (item.kind === 'plugin') {
149
- for (const k of ['plugin', 'header', 'prefix', 'source']) r[k] = item[k];
150
- r.value = item.params.map((p) => resolveParam(name, p));
151
- } else {
152
- r.value = val(rawValue(item), name);
153
- }
146
+ r.value = val(rawValue(item), name);
154
147
  busy.delete(name);
155
148
  done.set(name, r);
156
149
  return r;
157
150
  }
158
- function resolveParam(plugin, p) {
159
- const from = `${plugin}.${p.symbol}`;
160
- const read = lookup(p.from, from);
161
- const toggle = (p.flags ?? []).includes('toggle');
162
- if (toggle) {
163
- if (typeof read !== 'boolean') throw new Error(`${from}: a toggle reads a boolean, ${p.from} is ${JSON.stringify(read)}`);
164
- return { symbol: p.symbol, min: 0, max: 1, def: read ? 1 : 0, step: 1, unit: '', toggle: true };
165
- }
166
- if (!isObj(read)) throw new Error(`${from}: ${p.from} is not a travel`);
167
- let def = read.default;
168
- if (p.forKind !== undefined) def = read.byKind?.[p.forKind] ?? read.default;
169
- for (const [k, v] of [['min', read.min], ['max', read.max], ['default', def], ['step', read.step], ['unit', read.unit]]) {
170
- if (v === undefined) throw new Error(`${from}: ${p.from} declares no ${k}`);
171
- }
172
- return { symbol: p.symbol, min: read.min, max: read.max, def, step: read.step, unit: read.unit, toggle: false };
173
- }
174
151
  const out = new Map();
175
152
  for (const name of data.items.keys()) out.set(name, resolveItem(name));
176
153
  return out;
package/lib/fmt.mjs CHANGED
@@ -7,9 +7,8 @@
7
7
  */
8
8
 
9
9
  const ORDER = {
10
- item: ['kind', 'doc', 'why', 'unit', 'plugin', 'header', 'prefix', 'source', 'value', 'values', 'fields', 'travel', 'params', 'c'],
10
+ item: ['kind', 'doc', 'why', 'unit', 'value', 'values', 'fields', 'travel', 'c'],
11
11
  travel: ['min', 'max', 'step', 'unit', 'default', 'defaultFrom', 'byKind', 'doc', 'why'],
12
- param: ['symbol', 'from', 'forKind', 'flags', 'scale', 'since', 'doc'],
13
12
  c: ['alias', 'render'],
14
13
  alias: ['fact', 'field'],
15
14
  number: ['ref', 'derive', 'of', 'order'],
@@ -46,7 +45,6 @@ function item(it) {
46
45
  if ('value' in o) o.value = o.kind === 'sheet' ? sheet(o.value) : number(o.value);
47
46
  if (isObj(o.fields)) o.fields = Object.fromEntries(Object.entries(o.fields).map(([k, t]) => [k, isObj(t) ? travel(t) : t]));
48
47
  if (isObj(o.travel)) o.travel = travel(o.travel);
49
- if (Array.isArray(o.params)) o.params = o.params.map((p) => (isObj(p) ? ordered(p, ORDER.param) : p));
50
48
  if (isObj(o.c)) {
51
49
  o.c = ordered(o.c, ORDER.c);
52
50
  if (isObj(o.c.alias)) o.c.alias = ordered(o.c.alias, ORDER.alias);
package/lib/semver.mjs CHANGED
@@ -4,16 +4,24 @@
4
4
  * `omx-contract semver`: classify every change between two resolved declarations (spec §3.6) and
5
5
  * refuse a version whose bump is shorter than the largest class found.
6
6
  *
7
- * major an item, field, parameter or alias removed; a unit or kind changed; a travel narrowed; a
8
- * plugin's parameters reordered or changed; a constant a kernel computes with changed (a
7
+ * major a field or alias removed; an item removed (MINOR in the development phase, below); a
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
10
  * changed or removed)
11
- * minor an item, field, sheet leaf, parameter or list member added (appended); a travel widened;
11
+ * minor an item, field, sheet leaf or list member added (appended); a travel widened;
12
12
  * a default moved inside its travel; a step changed
13
13
  * patch doc and why text
14
14
  */
15
15
 
16
16
  const RANK = { none: 0, patch: 1, minor: 2, major: 3 };
17
+
18
+ /**
19
+ * The development-phase rule (openmixer spec 2026-10-05-omx-contract §3.6): while every consumer of
20
+ * omx-contract is a FreeMixer repository named here, removing an item is MINOR. The rule ends, and
21
+ * this becomes null so a removal is MAJOR again, in the release that names the first consumer
22
+ * outside the list.
23
+ */
24
+ export const DEVELOPMENT_PHASE = Object.freeze({ consumers: Object.freeze(['omx-dsp', 'omx-plugins', 'openmixer']) });
17
25
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
18
26
 
19
27
  function travelChanges(at, a, b, say) {
@@ -47,12 +55,16 @@ function leafChanges(at, a, b, say) {
47
55
  }
48
56
 
49
57
  /** `{ level, changes: [{ level, item, what }] }` from `prev` to `next` (Maps name -> resolved item). */
50
- export function classify(prev, next) {
58
+ export function classify(prev, next, { developmentPhase = DEVELOPMENT_PHASE } = {}) {
51
59
  const changes = [];
52
60
  for (const [name, a] of prev) {
53
61
  const say = (level, what) => changes.push({ level, item: name, what });
54
62
  const b = next.get(name);
55
- if (!b) { say('major', 'item removed or renamed'); continue; }
63
+ if (!b) {
64
+ if (developmentPhase) say('minor', `item removed or renamed (MINOR in the development phase: every consumer is ${developmentPhase.consumers.join(', ')})`);
65
+ else say('major', 'item removed or renamed');
66
+ continue;
67
+ }
56
68
  if (a.kind !== b.kind || (a.shape ?? '') !== (b.shape ?? '')) { say('major', `kind ${a.kind} -> ${b.kind}`); continue; }
57
69
  if ((a.unit ?? '') !== (b.unit ?? '')) say('major', `unit ${JSON.stringify(a.unit)} -> ${JSON.stringify(b.unit)}`);
58
70
  if (a.doc !== b.doc || (a.why ?? '') !== (b.why ?? '')) say('patch', 'doc');
@@ -80,17 +92,6 @@ export function classify(prev, next) {
80
92
  });
81
93
  if (b.value.length > a.value.length) say('minor', `${name}: ${b.value.length - a.value.length} member(s) appended`);
82
94
  break;
83
- case 'plugin':
84
- for (const k of ['plugin', 'header', 'prefix']) if (a[k] !== b[k]) say('major', `${k} ${a[k]} -> ${b[k]}`);
85
- if (a.source !== b.source) say('patch', 'source text');
86
- a.value.forEach((p, i) => {
87
- const q = b.value[i];
88
- if (!q) say('major', `parameter ${p.symbol} removed`);
89
- else if (q.symbol !== p.symbol) say('major', `parameter ${i} ${p.symbol} -> ${q.symbol} (reordered or renamed)`);
90
- else if (!same(p, q)) say(p.min < q.min || p.max > q.max || p.unit !== q.unit || p.toggle !== q.toggle ? 'major' : 'minor', `parameter ${p.symbol} changed`);
91
- });
92
- if (b.value.length > a.value.length) say('minor', `${b.value.length - a.value.length} parameter(s) appended`);
93
- break;
94
95
  default:
95
96
  break;
96
97
  }
package/lib/validate.mjs CHANGED
@@ -3,8 +3,7 @@
3
3
  /**
4
4
  * `omx-contract validate`: the schema, then the checks a schema cannot express (spec §3.3): every
5
5
  * reference resolves with no cycle; every default inside its travel; every C name the render spells
6
- * is unique; an item's `c.alias` names no other item; a plugin's parameters only grow (each `since`
7
- * no earlier than the one before it) and read what they say. Each failure names file, item and rule.
6
+ * is unique; an item's `c.alias` names no other item. Each failure names file, item and rule.
8
7
  */
9
8
  import { readFileSync } from 'node:fs';
10
9
  import { posix } from 'node:path';
@@ -21,7 +20,6 @@ export function schemaValidator() {
21
20
  return compiled;
22
21
  }
23
22
 
24
- const semverKey = (v) => v.split('.').map(Number).reduce((a, n) => a * 1e6 + n, 0);
25
23
  const constCase = (s) => s.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toUpperCase();
26
24
 
27
25
  /** Every problem of the loaded data: `[{ file, item, rule, message }]`; empty is valid. */
@@ -70,19 +68,6 @@ export function validateData(data) {
70
68
  if (resolved.has(spelled)) say(r.rel, r.name, 'alias', `c.alias spells ${spelled}, which is another item`);
71
69
  if (r.shape !== 'travel') say(r.rel, r.name, 'alias', 'c.alias is for a single travel');
72
70
  }
73
- if (r.kind === 'plugin') {
74
- const raw = data.items.get(r.name).item;
75
- const symbols = new Set();
76
- raw.params.forEach((p, i) => {
77
- if (symbols.has(p.symbol)) say(r.rel, r.name, 'param-unique', `parameter ${p.symbol} declared twice`);
78
- symbols.add(p.symbol);
79
- if (i > 0 && semverKey(p.since) < semverKey(raw.params[i - 1].since)) say(r.rel, r.name, 'append-only', `${p.symbol} (since ${p.since}) follows ${raw.params[i - 1].symbol} (since ${raw.params[i - 1].since}): parameters only grow at the end`);
80
- if (p.forKind !== undefined) {
81
- const t = resolved.get(p.from.split('.')[0])?.value;
82
- if (!t?.byKind) say(r.rel, r.name, 'for-kind', `${p.symbol}: ${p.from} declares no byKind`);
83
- }
84
- });
85
- }
86
71
  if (typeof r.doc !== 'string' || !r.doc.trim()) say(r.rel, r.name, 'doc', 'every item carries its doc');
87
72
  }
88
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmixer/omx-contract",
3
- "version": "1.0.0",
3
+ "version": "1.1.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
@@ -1,10 +1,11 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-or-later
2
2
  // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
3
  /**
4
- * The `c` target: `omxcontract/omx_contract_limits.h`, one `omxcontract/params/omx_<plugin>_params.h`
5
- * per plugin item and the shape they fill, `omxcontract/params/omx_plugin_param.h`. Every define is
6
- * spelled by openmixer's renderer, moved unchanged (c/contract-limits-render.mjs,
7
- * c/params-generate.mjs); only the frame (banner, section comments) is this repository's.
4
+ * The `c` target: `omxcontract/omx_contract_limits.h`. Every define is spelled by openmixer's
5
+ * renderer, moved unchanged (c/contract-limits-render.mjs); only the frame (banner, section
6
+ * comments) is this repository's. A plugin's parameter header is rendered by omx-plugins from its
7
+ * declaration; c/params-generate.mjs stays here only for renderSheetC, the frozen fixture of the
8
+ * round-trip proof.
8
9
  */
9
10
  import { readFileSync } from 'node:fs';
10
11
  import {
@@ -75,20 +76,9 @@ ${sections.map(([body, comment]) => `${comment}\n${body}`).join('\n\n')}
75
76
  ${d.STANDARD_SAMPLE_RATES ? ratesTail(d.STANDARD_SAMPLE_RATES) : ''}`;
76
77
  }
77
78
 
78
- /** The banner lines of a plugin's params header rendered from the data. */
79
- const producedBy = (r) => ` * Produced by \`omx-contract render --target c\` from FreeMixer/omx-contract's ${r.rel}
80
- * (${r.name}): ${r.source}.
81
- * Change the data, never this file. Order is append-only (omx-contract spec §3.3).`;
82
-
83
79
  /** Every file of the `c` target: `[{ path, text }]`, paths relative to the output directory. */
84
80
  export function renderC(resolved) {
85
- const files = [{ path: 'omxcontract/omx_contract_limits.h', text: renderLimitsHeader(resolved) }];
86
- const plugins = doors(resolved).PLUGINS;
87
- for (const r of plugins) {
88
- files.push({ path: `omxcontract/params/${r.header}`, text: generate({ header: r.header, prefix: r.prefix, source: r.source, producedBy: producedBy(r) }, r.value) });
89
- }
90
- if (plugins.length) files.push({ path: 'omxcontract/params/omx_plugin_param.h', text: PLUGIN_PARAM_H });
91
- return files;
81
+ return [{ path: 'omxcontract/omx_contract_limits.h', text: renderLimitsHeader(resolved) }];
92
82
  }
93
83
 
94
84
  /**
package/render/doors.mjs CHANGED
@@ -38,7 +38,7 @@ export function doors(resolved) {
38
38
  .sort(([a], [b]) => a.localeCompare(b));
39
39
  const byMacro = new Map();
40
40
  for (const r of items) {
41
- if (isNativeTable(r) || r.kind === 'plugin' || r.kind === 'list') continue;
41
+ if (isNativeTable(r) || r.kind === 'list') continue;
42
42
  const leaves = numberLeaves(r.value, r.name, r.name.replace(/_(KERNEL|LIMITS)$/, ''));
43
43
  if (leaves === undefined) continue;
44
44
  for (const s of leaves) {
@@ -58,7 +58,6 @@ export function doors(resolved) {
58
58
  if (!ALIASES.has(fact)) ALIASES.set(fact, {});
59
59
  ALIASES.get(fact)[field] = r.value;
60
60
  }
61
- const PLUGINS = items.filter((r) => r.kind === 'plugin');
62
61
  const STANDARD_SAMPLE_RATES = resolved.get('STANDARD_SAMPLE_RATES')?.value;
63
- return { NATIVE_LIMIT_TABLES, DECLARED_SCALARS, DECLARED_LISTS, ALIASES, PLUGINS, STANDARD_SAMPLE_RATES };
62
+ return { NATIVE_LIMIT_TABLES, DECLARED_SCALARS, DECLARED_LISTS, ALIASES, STANDARD_SAMPLE_RATES };
64
63
  }
package/render/ts.mjs CHANGED
@@ -7,8 +7,7 @@
7
7
 
8
8
  /** The value a TypeScript reader sees for one resolved item. */
9
9
  export function tsValue(r) {
10
- if (r.kind !== 'plugin') return r.value;
11
- return { plugin: r.plugin, header: r.header, prefix: r.prefix, params: r.value };
10
+ return r.value;
12
11
  }
13
12
 
14
13
  /** The literal type of a JSON value, every member readonly. */
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://github.com/FreeMixer/omx-contract/raw/main/schema/omx-contract.schema.json",
4
4
  "title": "omx-contract data file",
5
- "description": "A map from an export name to one item: a scalar, a travels table or travel, a sheet, a list or a plugin's parameter selection. GPL-3.0-or-later.",
5
+ "description": "A map from an export name to one item: a scalar, a travels table or travel, a sheet or a list. GPL-3.0-or-later.",
6
6
  "type": "object",
7
7
  "properties": {
8
8
  "$schema": { "type": "string" }
@@ -11,8 +11,8 @@
11
11
  "additionalProperties": { "$ref": "#/$defs/item" },
12
12
  "$defs": {
13
13
  "unit": {
14
- "description": "The closed list of units the items use; \"\" is a ratio or a fraction.",
15
- "enum": ["dB", "dBFS", "ms", "s", "min", "Hz", "oct", "cents", "%", ""]
14
+ "description": "The closed list of units the items use; \"x\" is a multiplier, \"\" is a ratio or a fraction.",
15
+ "enum": ["dB", "dBFS", "ms", "s", "min", "Hz", "oct", "cents", "%", "x", ""]
16
16
  },
17
17
  "text": { "type": "string", "minLength": 1 },
18
18
  "name": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" },
@@ -97,7 +97,7 @@
97
97
  "fields": {
98
98
  "type": "object",
99
99
  "propertyNames": { "pattern": "^[a-z][A-Za-z0-9]*$" },
100
- "additionalProperties": { "$ref": "#/$defs/travel" },
100
+ "additionalProperties": { "oneOf": [{ "$ref": "#/$defs/travel" }, { "$ref": "#/$defs/ref" }] },
101
101
  "minProperties": 1
102
102
  },
103
103
  "travel": { "$ref": "#/$defs/travel" },
@@ -149,45 +149,15 @@
149
149
  "required": ["kind", "doc", "unit", "values"],
150
150
  "additionalProperties": false
151
151
  },
152
- "param": {
153
- "type": "object",
154
- "properties": {
155
- "symbol": { "type": "string", "pattern": "^[a-z][A-Za-z0-9]*$" },
156
- "from": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*(\\.[a-z][A-Za-z0-9]*)?$" },
157
- "forKind": { "type": "string", "pattern": "^[a-z][A-Za-z0-9]*$" },
158
- "flags": { "type": "array", "items": { "enum": ["toggle"] }, "uniqueItems": true },
159
- "scale": { "enum": ["linear", "log"] },
160
- "since": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
161
- "doc": { "$ref": "#/$defs/text" }
162
- },
163
- "required": ["symbol", "from", "scale", "since"],
164
- "additionalProperties": false
165
- },
166
- "plugin": {
167
- "type": "object",
168
- "properties": {
169
- "kind": { "const": "plugin" },
170
- "doc": { "$ref": "#/$defs/text" },
171
- "why": { "$ref": "#/$defs/text" },
172
- "plugin": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
173
- "header": { "type": "string", "pattern": "^omx_[a-z0-9_]+_params\\.h$" },
174
- "prefix": { "type": "string", "pattern": "^OMX_[A-Z0-9_]+$" },
175
- "source": { "$ref": "#/$defs/text" },
176
- "params": { "type": "array", "items": { "$ref": "#/$defs/param" }, "minItems": 1 }
177
- },
178
- "required": ["kind", "doc", "plugin", "header", "prefix", "source", "params"],
179
- "additionalProperties": false
180
- },
181
152
  "item": {
182
153
  "type": "object",
183
154
  "required": ["kind"],
184
- "properties": { "kind": { "enum": ["scalar", "travels", "sheet", "list", "plugin"] } },
155
+ "properties": { "kind": { "enum": ["scalar", "travels", "sheet", "list"] } },
185
156
  "allOf": [
186
157
  { "if": { "properties": { "kind": { "const": "scalar" } } }, "then": { "$ref": "#/$defs/scalar" } },
187
158
  { "if": { "properties": { "kind": { "const": "travels" } } }, "then": { "$ref": "#/$defs/travels" } },
188
159
  { "if": { "properties": { "kind": { "const": "sheet" } } }, "then": { "$ref": "#/$defs/sheet" } },
189
- { "if": { "properties": { "kind": { "const": "list" } } }, "then": { "$ref": "#/$defs/list" } },
190
- { "if": { "properties": { "kind": { "const": "plugin" } } }, "then": { "$ref": "#/$defs/plugin" } }
160
+ { "if": { "properties": { "kind": { "const": "list" } } }, "then": { "$ref": "#/$defs/list" } }
191
161
  ]
192
162
  }
193
163
  }