@onda-lang/webaudio 0.8.21 → 0.8.22

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 CHANGED
@@ -56,8 +56,14 @@ block, independently of Web Audio callback sizes; durations at most one block ar
56
56
  Integer, boolean, and stepped controls update directly. Initial parameters and reinitialization
57
57
  settle immediately. A new target restarts from the published value, while repeated writes of the
58
58
  same target preserve its deadline. Host ramps are excluded from processor snapshots. The browser
59
- playground uses `0.03` seconds (30 ms); explicit DSP smoothing can be used for interpolation within
60
- an audio block.
59
+ playground defaults to `0.03` seconds (30 ms), with a live **Smooth** control in its Params header
60
+ that remembers the duration in the browser. Explicit DSP smoothing can interpolate within a block.
61
+
62
+ `setParamSmoothingSeconds(seconds)` changes the duration live without recompilation or
63
+ reinitialization. Active ramps restart from the current published value with the new duration.
64
+ Setting 0 (or a duration at most one block) settles active targets immediately and disables ramps;
65
+ smoothing can be enabled later. The duration must be finite and nonnegative, and affects continuous
66
+ float writes from UI controls and automation alike.
61
67
 
62
68
  `resetParams()` restores all declared defaults immediately and cancels host ramps without resetting
63
69
  DSP state.
package/api.md CHANGED
@@ -223,6 +223,10 @@ The adapter exposes its `node` and validated `metadata`, plus these operations:
223
223
  - `setParam(nameOrIndex, plain)` and `setParamNormalized(nameOrIndex, normalized)` for whole
224
224
  parameters.
225
225
  - `resetParams()` to restore parameter defaults immediately and cancel host ramps, retaining DSP state.
226
+ - `setParamSmoothingSeconds(seconds)` to change the host duration live without recompiling or
227
+ reinitializing DSP state. Finite, nonnegative durations are required. Active ramps restart from
228
+ their current values using the new duration; 0 (or a duration at most one block) settles their
229
+ targets immediately. Smoothing can be enabled after starting with 0.
226
230
  - `setParamElement(nameOrIndex, element, plain)` and
227
231
  `setParamElementNormalized(nameOrIndex, element, normalized)` for one scalar or fixed-array
228
232
  element.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onda-lang/webaudio",
3
- "version": "0.8.21",
3
+ "version": "0.8.22",
4
4
  "type": "module",
5
5
  "description": "Optional Web Audio adapter for Onda processor WebAssembly artifacts",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "prepack": "node ../../scripts/stage-web-api-docs.mjs"
35
35
  },
36
36
  "dependencies": {
37
- "@onda-lang/processor-abi": "0.8.21"
37
+ "@onda-lang/processor-abi": "0.8.22"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
package/src/index.d.ts CHANGED
@@ -134,6 +134,8 @@ export class OndaAudioProcessor {
134
134
  setParam(param: string | number, value: unknown): Promise<any>;
135
135
  /** Restore all parameter defaults immediately, cancelling host ramps and retaining DSP state. */
136
136
  resetParams(): Promise<any>;
137
+ /** Change host smoothing live, restarting active ramps from their current values; 0 settles them immediately. */
138
+ setParamSmoothingSeconds(seconds: number): Promise<any>;
137
139
  /** Set one scalar or fixed-array parameter element without modifying its siblings. */
138
140
  setParamElement(param: string | number, element: number, value: unknown): Promise<any>;
139
141
  /** Map a host value in [0, 1] through the descriptor and set the resulting plain value. */
package/src/index.js CHANGED
@@ -660,6 +660,16 @@ export class OndaAudioProcessor {
660
660
  }
661
661
  }
662
662
 
663
+ setParamSmoothingSeconds(seconds) {
664
+ try {
665
+ paramSmoothingSamples(seconds, this.metadata?.compile?.sample_rate ?? 1,
666
+ this.metadata?.compile?.block_size ?? 0);
667
+ return this.request("set-param-smoothing", { seconds });
668
+ } catch (error) {
669
+ return Promise.reject(error);
670
+ }
671
+ }
672
+
663
673
  resetParams() {
664
674
  return this.request("reset-params");
665
675
  }
@@ -48,29 +48,55 @@ export class ParamSmoothing {
48
48
  this.started = false;
49
49
  this.addresses = new Map();
50
50
  this.entries = [];
51
- if (samples > 0) {
52
- for (const param of params) {
53
- if ((param.scalar !== "f32" && param.scalar !== "f64")
54
- || param.param_control?.step_count != null) continue;
55
- const elements = [];
56
- const f32 = param.scalar === "f32";
57
- for (let element = 0; element < param.array_len; element += 1) {
58
- const offset = Number(param.byte_offset) + element * (f32 ? 4 : 8);
59
- const value = f32 ? view.getFloat32(paramsPtr + offset, true)
60
- : view.getFloat64(paramsPtr + offset, true);
61
- elements.push(this.entries.length);
62
- this.entries.push({ offset, f32, current: value, start: value,
63
- target: value, elapsed: samples, active: false });
64
- }
65
- this.addresses.set(param, elements);
51
+ for (const param of params) {
52
+ if ((param.scalar !== "f32" && param.scalar !== "f64")
53
+ || param.param_control?.step_count != null) continue;
54
+ const elements = [];
55
+ const f32 = param.scalar === "f32";
56
+ for (let element = 0; element < param.array_len; element += 1) {
57
+ const offset = Number(param.byte_offset) + element * (f32 ? 4 : 8);
58
+ const value = f32 ? view.getFloat32(paramsPtr + offset, true)
59
+ : view.getFloat64(paramsPtr + offset, true);
60
+ elements.push(this.entries.length);
61
+ this.entries.push({ offset, f32, current: value, start: value,
62
+ target: value, elapsed: samples, active: false });
66
63
  }
64
+ this.addresses.set(param, elements);
67
65
  }
68
66
  this.active = new Uint32Array(this.entries.length);
69
67
  this.activeCount = 0;
70
68
  }
71
69
 
72
70
  isEnabledFor(param) {
73
- return this.addresses.has(param);
71
+ return this.samples > 0 && this.addresses.has(param);
72
+ }
73
+
74
+ setDuration(samples, view, paramsPtr) {
75
+ if (samples === this.samples) return;
76
+ // Bulk writes bypass ramp state while disabled; read the actual DSP values.
77
+ if (this.samples === 0) this.readTargets(view, paramsPtr);
78
+ const previousSamples = this.samples;
79
+ this.samples = samples;
80
+ this.inverseSamples = 1 / Math.max(samples, 1);
81
+ for (let cursor = 0; cursor < this.activeCount; cursor += 1) {
82
+ const entry = this.entries[this.active[cursor]];
83
+ if (samples === 0) {
84
+ this.write(entry, entry.target, view, paramsPtr);
85
+ entry.active = false;
86
+ }
87
+ entry.start = entry.current;
88
+ entry.elapsed = entry.elapsed === previousSamples ? samples : 0;
89
+ }
90
+ if (samples === 0) this.activeCount = 0;
91
+ }
92
+
93
+ readTargets(view, paramsPtr) {
94
+ for (const entry of this.entries) {
95
+ entry.current = entry.target = entry.f32
96
+ ? view.getFloat32(paramsPtr + entry.offset, true)
97
+ : view.getFloat64(paramsPtr + entry.offset, true);
98
+ entry.start = entry.current;
99
+ }
74
100
  }
75
101
 
76
102
  setTarget(param, element, value, initialized) {
@@ -78,7 +104,7 @@ export class ParamSmoothing {
78
104
  if (id === undefined) return false;
79
105
  const entry = this.entries[id];
80
106
  const target = entry.f32 ? Math.fround(Number(value)) : Number(value);
81
- if (!initialized || !this.started || !Number.isFinite(target) || !Number.isFinite(entry.current)) {
107
+ if (this.samples === 0 || !initialized || !this.started || !Number.isFinite(target) || !Number.isFinite(entry.current)) {
82
108
  entry.current = target;
83
109
  entry.target = target;
84
110
  entry.elapsed = this.samples;
@@ -123,6 +149,7 @@ export class ParamSmoothing {
123
149
  }
124
150
 
125
151
  settle(view, paramsPtr) {
152
+ if (this.samples === 0) this.readTargets(view, paramsPtr);
126
153
  this.activeCount = 0;
127
154
  this.started = false;
128
155
  for (const entry of this.entries) {
@@ -134,10 +161,7 @@ export class ParamSmoothing {
134
161
  }
135
162
 
136
163
  reset(view, paramsPtr) {
137
- for (const entry of this.entries) {
138
- entry.target = entry.f32 ? view.getFloat32(paramsPtr + entry.offset, true)
139
- : view.getFloat64(paramsPtr + entry.offset, true);
140
- }
164
+ this.readTargets(view, paramsPtr);
141
165
  this.settle(view, paramsPtr);
142
166
  }
143
167
 
package/src/worklet.js CHANGED
@@ -748,6 +748,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
748
748
  message.element ?? null,
749
749
  );
750
750
  this.postResponse(message, { type: "onda-ok", operation: message.type });
751
+ } else if (message.type === "set-param-smoothing") {
752
+ const samples = paramSmoothingSamples(message.seconds, this.compileSampleRate, this.blockSize);
753
+ this.paramSmoothing.setDuration(samples, this.memoryView(), this.paramsPtr);
754
+ this.postResponse(message, { type: "onda-ok", operation: message.type });
751
755
  } else if (message.type === "reset-params") {
752
756
  this.writeParamDefaults();
753
757
  this.paramSmoothing.reset(this.memoryView(), this.paramsPtr);