@onda-lang/webaudio 0.8.1 → 0.8.3
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 +4 -2
- package/api.md +6 -1
- package/package.json +2 -2
- package/src/index.js +29 -12
- package/src/param-metadata.js +18 -0
- package/src/worklet.js +6 -7
package/README.md
CHANGED
|
@@ -75,10 +75,12 @@ const sliderValueFor440Hz = paramPlainToNormalized(cutoff, 440);
|
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
The helpers preserve exact endpoints, clamp and snap consistently with adapter writes, and reject
|
|
78
|
-
|
|
78
|
+
numeric parameters without a host-control domain. Select array elements with indexed names such
|
|
79
|
+
as `offsets[0]`, or supply an exact-length array to `setParam("offsets", values)`. `scale`, `curve`, `unit`, `step_repr`,
|
|
79
80
|
and `step_count` remain available on each parameter's descriptor metadata for control construction
|
|
80
81
|
and formatting. For repeated UI conversion, `createParamControl(param)` prepares and validates the
|
|
81
|
-
descriptor once and returns bound conversion methods
|
|
82
|
+
descriptor once and returns bound conversion methods; pass an element index as its second argument
|
|
83
|
+
for an array.
|
|
82
84
|
|
|
83
85
|
Controlled `i64` domains use the descriptor's exact binary64 integer range. Full-width unranged
|
|
84
86
|
`i64` values continue to use `bigint` when written directly.
|
package/api.md
CHANGED
|
@@ -119,7 +119,11 @@ versions rather than guessing layout compatibility.
|
|
|
119
119
|
### Parameter controls
|
|
120
120
|
|
|
121
121
|
`createParamControl(metadata)` validates one scalar parameter descriptor and returns a reusable
|
|
122
|
-
`OndaPreparedParamControl`. `
|
|
122
|
+
`OndaPreparedParamControl`. For an array, `createParamControl(metadata, index)` prepares one element;
|
|
123
|
+
`paramElementMetadata(metadata, index)` projects its scalar descriptor, default, and storage offset.
|
|
124
|
+
The Web Audio adapter accepts indexed names such as `offsets[1]` in `setParam` and
|
|
125
|
+
`setParamNormalized`, and exact-length arrays in `setParam("offsets", values)`.
|
|
126
|
+
`createParamDomain(domain)` does the same from already-decoded values.
|
|
123
127
|
Prepared controls expose `constrainPlain`, `normalizedToPlain`, and `plainToNormalized` methods.
|
|
124
128
|
|
|
125
129
|
The one-shot `constrainParamPlain`, `paramNormalizedToPlain`, and `paramPlainToNormalized`
|
|
@@ -271,6 +275,7 @@ PROCESSOR_INIT_PRESERVE_PINNED
|
|
|
271
275
|
PROCESSOR_SNAPSHOT_FORMAT_VERSION
|
|
272
276
|
constrainParamPlain
|
|
273
277
|
createParamControl
|
|
278
|
+
paramElementMetadata
|
|
274
279
|
createParamDomain
|
|
275
280
|
createProcessorArtifactFiles
|
|
276
281
|
decodeDelegateRecords
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onda-lang/webaudio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
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.
|
|
37
|
+
"@onda-lang/processor-abi": "0.8.3"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { paramAddress } from "./param-metadata.js";
|
|
1
2
|
import {
|
|
2
3
|
createParamControl,
|
|
4
|
+
paramElementMetadata,
|
|
3
5
|
decodeDelegateRecords,
|
|
4
6
|
formatPrintRecords,
|
|
5
7
|
validateProcessorArtifact,
|
|
@@ -152,18 +154,24 @@ function configuredExecutionOutputCapacity(
|
|
|
152
154
|
return capacity;
|
|
153
155
|
}
|
|
154
156
|
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
function paramElementInfo(info, element, cache = null) {
|
|
158
|
+
if (!cache) return paramElementMetadata(info, element);
|
|
159
|
+
let elements = cache.get(info);
|
|
160
|
+
if (!elements) {
|
|
161
|
+
elements = new Map();
|
|
162
|
+
cache.set(info, elements);
|
|
161
163
|
}
|
|
162
|
-
|
|
164
|
+
if (!elements.has(element)) elements.set(element, paramElementMetadata(info, element));
|
|
165
|
+
return elements.get(element);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function paramInfoFor(params, selector, cache = null) {
|
|
169
|
+
const { info, element } = paramAddress(params, selector);
|
|
170
|
+
return element === null ? info : paramElementInfo(info, element, cache);
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
function preparedParamControl(info, cache = null) {
|
|
166
|
-
if (
|
|
174
|
+
if (info.type_repr !== info.scalar) return null;
|
|
167
175
|
if (info.scalar !== "bool" && info.param_control === null) return null;
|
|
168
176
|
let control = cache?.get(info);
|
|
169
177
|
if (!control) {
|
|
@@ -173,7 +181,15 @@ function preparedParamControl(info, cache = null) {
|
|
|
173
181
|
return control;
|
|
174
182
|
}
|
|
175
183
|
|
|
176
|
-
function constrainParamValue(info, value, cache = null) {
|
|
184
|
+
function constrainParamValue(info, value, cache = null, elements = null) {
|
|
185
|
+
if (info.type_repr !== info.scalar) {
|
|
186
|
+
if ((!Array.isArray(value) && !ArrayBuffer.isView(value)) || value.length !== info.array_len) {
|
|
187
|
+
throw new Error(`Onda parameter '${info.name}' requires exactly ${info.array_len} values`);
|
|
188
|
+
}
|
|
189
|
+
return Array.from(value, (item, index) => constrainParamValue(
|
|
190
|
+
paramElementInfo(info, index, elements), item, cache, elements,
|
|
191
|
+
));
|
|
192
|
+
}
|
|
177
193
|
return preparedParamControl(info, cache)?.constrainPlain(value) ?? value;
|
|
178
194
|
}
|
|
179
195
|
|
|
@@ -291,6 +307,7 @@ export class OndaAudioProcessor {
|
|
|
291
307
|
this.metadata = metadata;
|
|
292
308
|
this.paramInfo = metadata?.metadata?.params ?? null;
|
|
293
309
|
this.paramControls = new WeakMap();
|
|
310
|
+
this.paramElements = new WeakMap();
|
|
294
311
|
this.nextRequestId = 1;
|
|
295
312
|
this.pending = new Map();
|
|
296
313
|
this.delegateListeners = new Set();
|
|
@@ -580,10 +597,10 @@ export class OndaAudioProcessor {
|
|
|
580
597
|
if (!Array.isArray(this.paramInfo)) {
|
|
581
598
|
return this.request("set-param", { param, value });
|
|
582
599
|
}
|
|
583
|
-
const info = paramInfoFor(this.paramInfo ?? [], param);
|
|
600
|
+
const info = paramInfoFor(this.paramInfo ?? [], param, this.paramElements);
|
|
584
601
|
return this.request("set-param", {
|
|
585
602
|
param,
|
|
586
|
-
value: constrainParamValue(info, value, this.paramControls),
|
|
603
|
+
value: constrainParamValue(info, value, this.paramControls, this.paramElements),
|
|
587
604
|
});
|
|
588
605
|
} catch (error) {
|
|
589
606
|
return Promise.reject(error);
|
|
@@ -597,7 +614,7 @@ export class OndaAudioProcessor {
|
|
|
597
614
|
"setParamNormalized requires processor metadata; construct the adapter with createOndaAudioProcessor()",
|
|
598
615
|
);
|
|
599
616
|
}
|
|
600
|
-
const info = paramInfoFor(this.paramInfo, param);
|
|
617
|
+
const info = paramInfoFor(this.paramInfo, param, this.paramElements);
|
|
601
618
|
const control = preparedParamControl(info, this.paramControls);
|
|
602
619
|
if (!control) {
|
|
603
620
|
throw new Error(`Onda parameter '${info.name}' has no scalar host-control domain`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Shared by the adapter and worklet; keep this module independent of package
|
|
2
|
+
// imports so worklets also load from a plain static HTTP directory.
|
|
3
|
+
export function paramAddress(params, selector) {
|
|
4
|
+
const direct = Number.isInteger(selector) ? params[selector] : params.find((param) => param.name === selector);
|
|
5
|
+
if (direct) return { info: direct, element: null };
|
|
6
|
+
if (typeof selector === "string") {
|
|
7
|
+
const match = /^(.*)\[(0|[1-9][0-9]*)\]$/.exec(selector);
|
|
8
|
+
const root = match && params.find((param) => param.name === match[1] && param.type_repr !== param.scalar);
|
|
9
|
+
if (root) {
|
|
10
|
+
const element = Number(match[2]);
|
|
11
|
+
if (!Number.isSafeInteger(element) || element >= root.array_len) {
|
|
12
|
+
throw new Error(`Onda parameter '${root.name}' element index is out of bounds`);
|
|
13
|
+
}
|
|
14
|
+
return { info: root, element };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`unknown Onda parameter '${String(selector)}'`);
|
|
18
|
+
}
|
package/src/worklet.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { paramAddress } from "./param-metadata.js";
|
|
1
2
|
import {
|
|
2
3
|
EXECUTION_OPERATION_EVENT,
|
|
3
4
|
EXECUTION_OPERATION_INIT,
|
|
@@ -477,13 +478,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
477
478
|
}
|
|
478
479
|
|
|
479
480
|
setParam(selector, value) {
|
|
480
|
-
const
|
|
481
|
-
? selector
|
|
482
|
-
: this.paramInfo.findIndex((param) => param.name === selector);
|
|
483
|
-
const param = this.paramInfo[paramId];
|
|
484
|
-
if (!param) {
|
|
485
|
-
throw new Error(`unknown Onda parameter '${String(selector)}'`);
|
|
486
|
-
}
|
|
481
|
+
const { info: param, element } = paramAddress(this.paramInfo, selector);
|
|
487
482
|
if (value === undefined) {
|
|
488
483
|
throw new Error(`Onda parameter '${param.name}' requires a value`);
|
|
489
484
|
}
|
|
@@ -502,6 +497,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
502
497
|
) {
|
|
503
498
|
throw new Error(`Onda parameter '${param.name}' has invalid storage metadata`);
|
|
504
499
|
}
|
|
500
|
+
if (element !== null) {
|
|
501
|
+
this.writeScalar(this.paramsPtr + offset + element * this.scalarByteSize(param.scalar), param.scalar, value, this.memoryView());
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
505
504
|
this.writeStorage(
|
|
506
505
|
this.paramsPtr + offset,
|
|
507
506
|
param,
|