@onda-lang/webaudio 0.8.2 → 0.8.4
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 +39 -4
- package/api.md +37 -2
- package/package.json +2 -2
- package/src/index.js +41 -13
- package/src/param-metadata.js +18 -0
- package/src/worklet.js +26 -84
package/README.md
CHANGED
|
@@ -75,15 +75,15 @@ 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.
|
|
85
|
-
Event `i64` values accept bigint, safe integers, or decimal integer strings and reject values
|
|
86
|
-
outside the signed 64-bit range.
|
|
87
87
|
|
|
88
88
|
The artifact must be compiled for exactly `audioContext.sampleRate`; the adapter rejects a mismatch
|
|
89
89
|
before registering the node so sample-rate-derived language semantics cannot silently drift. A Web
|
|
@@ -95,6 +95,41 @@ Call `processor.close()` when the adapter is no longer used. Closing is idempote
|
|
|
95
95
|
rejects pending requests, removes listeners, and makes subsequent operations fail immediately. The
|
|
96
96
|
wrapped `AudioWorkletNode` and `AudioContext` remain caller-owned.
|
|
97
97
|
|
|
98
|
+
## Events
|
|
99
|
+
|
|
100
|
+
`trigger(nameOrIndex, values?)` accepts event parameters as an ordered array or an object keyed by
|
|
101
|
+
parameter name. Nominal structs are objects containing exactly their declared fields; tuples, fixed
|
|
102
|
+
arrays, and runtime slices are sequences. The adapter validates and encodes the complete logical
|
|
103
|
+
value on the main thread before transferring its packed bytes to the worklet.
|
|
104
|
+
|
|
105
|
+
For this Onda declaration:
|
|
106
|
+
|
|
107
|
+
```onda
|
|
108
|
+
struct Note:
|
|
109
|
+
frequency = 440.0
|
|
110
|
+
velocity = 1.0
|
|
111
|
+
|
|
112
|
+
init:
|
|
113
|
+
current_note: Note
|
|
114
|
+
|
|
115
|
+
event note_on(note: Note):
|
|
116
|
+
current_note = note
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
the corresponding host call is:
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
await processor.trigger("note_on", {
|
|
123
|
+
note: { frequency: 523.25, velocity: 0.7 },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Fixed arrays must have their declared length. Slice lengths may vary up to the configured event
|
|
128
|
+
payload capacity. `i64` leaves accept `bigint`, exact safe integers, or decimal integer strings and
|
|
129
|
+
reject values outside the signed 64-bit range. Missing top-level arguments use declared defaults;
|
|
130
|
+
structs and struct arrays have no event defaults and must be supplied in full. Input rejected during
|
|
131
|
+
worklet preflight leaves processor state usable and rejects the returned promise.
|
|
132
|
+
|
|
98
133
|
## Prints
|
|
99
134
|
|
|
100
135
|
Authored `print(...)` occurrences leave generated execution as bounded typed records. The worklet
|
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`
|
|
@@ -135,7 +139,8 @@ real-time execution.
|
|
|
135
139
|
|
|
136
140
|
`writeDelegateBatch` and `writePrintBatch` initialize reusable batch descriptors.
|
|
137
141
|
`writeExecutionOutput` connects their addresses. Call `resetExecutionOutput` immediately before
|
|
138
|
-
every generated init
|
|
142
|
+
every generated init or process entry. Generated event entries reset after successful input
|
|
143
|
+
preflight instead, so rejected input preserves existing records. After a successful generated call,
|
|
139
144
|
`readDelegateBatch` or `readPrintBatch` validates the result counters.
|
|
140
145
|
`decodeDelegateRecords` converts delegate payloads using `metadata.delegates`.
|
|
141
146
|
`decodePrintRecords` preserves primitive types and source sites; `formatPrintRecords` and
|
|
@@ -271,6 +276,7 @@ PROCESSOR_INIT_PRESERVE_PINNED
|
|
|
271
276
|
PROCESSOR_SNAPSHOT_FORMAT_VERSION
|
|
272
277
|
constrainParamPlain
|
|
273
278
|
createParamControl
|
|
279
|
+
paramElementMetadata
|
|
274
280
|
createParamDomain
|
|
275
281
|
createProcessorArtifactFiles
|
|
276
282
|
decodeDelegateRecords
|
|
@@ -291,6 +297,17 @@ validateProcessorModule
|
|
|
291
297
|
writeDelegateBatch
|
|
292
298
|
writeExecutionOutput
|
|
293
299
|
writePrintBatch
|
|
300
|
+
OndaPayloadType
|
|
301
|
+
OndaPayloadField
|
|
302
|
+
OndaPayloadSchema
|
|
303
|
+
OndaPayloadDefault
|
|
304
|
+
OndaPayloadValue
|
|
305
|
+
OndaPayloadSizes
|
|
306
|
+
PayloadPlan
|
|
307
|
+
canonicalF32Number
|
|
308
|
+
EVENT_INPUT_SIZE_BYTES
|
|
309
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED
|
|
310
|
+
writeEventInput
|
|
294
311
|
<!-- END WEB API onda_processor_abi -->
|
|
295
312
|
|
|
296
313
|
### `@onda-lang/binaryen-web`
|
|
@@ -405,3 +422,21 @@ paramNormalizedToPlain
|
|
|
405
422
|
paramPlainToNormalized
|
|
406
423
|
registerOndaAudioWorklet
|
|
407
424
|
<!-- END WEB API onda_webaudio -->
|
|
425
|
+
|
|
426
|
+
### Structured event and delegate values
|
|
427
|
+
|
|
428
|
+
`PayloadPlan` prepares a recursive message schema once. `encode(values)` accepts ordered parameter
|
|
429
|
+
arrays or objects keyed by parameter name; structs are objects containing exactly their declared
|
|
430
|
+
own fields, and arrays/tuples are sequences. Unknown named parameters and excess positional values
|
|
431
|
+
are rejected. Missing event arguments use declared constant defaults.
|
|
432
|
+
Supply `i64` as `bigint`, an exact safe integer, or a decimal string. `decode(bytes)` returns named
|
|
433
|
+
nested values with `bigint` for `i64`. Plans own immutable schema snapshots.
|
|
434
|
+
|
|
435
|
+
`requiredWorkspace(bytes)` preflights packed little-endian input. `sizes(lengths)` reports wire
|
|
436
|
+
and aligned workspace sizes without allocating payload storage. Low-level wasm hosts provision
|
|
437
|
+
both regions and a 16-byte descriptor, then call `writeEventInput` before invoking the event.
|
|
438
|
+
Rejected input returns `PROCESSOR_EXECUTION_INPUT_REJECTED` (2) and leaves processor state usable.
|
|
439
|
+
`OndaAudioProcessor.trigger` performs logical encoding before transferring bytes to the worklet.
|
|
440
|
+
Decoded `f32` leaves use `canonicalF32Number(value)`: the returned JavaScript number has the
|
|
441
|
+
shortest decimal representation that round-trips to the same `f32`, so JSON and controls do not
|
|
442
|
+
expose an irrelevant widened binary tail.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onda-lang/webaudio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
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.4"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { paramAddress } from "./param-metadata.js";
|
|
1
2
|
import {
|
|
3
|
+
PayloadPlan,
|
|
2
4
|
createParamControl,
|
|
5
|
+
paramElementMetadata,
|
|
3
6
|
decodeDelegateRecords,
|
|
4
7
|
formatPrintRecords,
|
|
5
8
|
validateProcessorArtifact,
|
|
@@ -152,18 +155,24 @@ function configuredExecutionOutputCapacity(
|
|
|
152
155
|
return capacity;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
function paramElementInfo(info, element, cache = null) {
|
|
159
|
+
if (!cache) return paramElementMetadata(info, element);
|
|
160
|
+
let elements = cache.get(info);
|
|
161
|
+
if (!elements) {
|
|
162
|
+
elements = new Map();
|
|
163
|
+
cache.set(info, elements);
|
|
161
164
|
}
|
|
162
|
-
|
|
165
|
+
if (!elements.has(element)) elements.set(element, paramElementMetadata(info, element));
|
|
166
|
+
return elements.get(element);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function paramInfoFor(params, selector, cache = null) {
|
|
170
|
+
const { info, element } = paramAddress(params, selector);
|
|
171
|
+
return element === null ? info : paramElementInfo(info, element, cache);
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
function preparedParamControl(info, cache = null) {
|
|
166
|
-
if (
|
|
175
|
+
if (info.type_repr !== info.scalar) return null;
|
|
167
176
|
if (info.scalar !== "bool" && info.param_control === null) return null;
|
|
168
177
|
let control = cache?.get(info);
|
|
169
178
|
if (!control) {
|
|
@@ -173,7 +182,15 @@ function preparedParamControl(info, cache = null) {
|
|
|
173
182
|
return control;
|
|
174
183
|
}
|
|
175
184
|
|
|
176
|
-
function constrainParamValue(info, value, cache = null) {
|
|
185
|
+
function constrainParamValue(info, value, cache = null, elements = null) {
|
|
186
|
+
if (info.type_repr !== info.scalar) {
|
|
187
|
+
if ((!Array.isArray(value) && !ArrayBuffer.isView(value)) || value.length !== info.array_len) {
|
|
188
|
+
throw new Error(`Onda parameter '${info.name}' requires exactly ${info.array_len} values`);
|
|
189
|
+
}
|
|
190
|
+
return Array.from(value, (item, index) => constrainParamValue(
|
|
191
|
+
paramElementInfo(info, index, elements), item, cache, elements,
|
|
192
|
+
));
|
|
193
|
+
}
|
|
177
194
|
return preparedParamControl(info, cache)?.constrainPlain(value) ?? value;
|
|
178
195
|
}
|
|
179
196
|
|
|
@@ -290,7 +307,9 @@ export class OndaAudioProcessor {
|
|
|
290
307
|
this.node = node;
|
|
291
308
|
this.metadata = metadata;
|
|
292
309
|
this.paramInfo = metadata?.metadata?.params ?? null;
|
|
310
|
+
this.eventPlans = new WeakMap();
|
|
293
311
|
this.paramControls = new WeakMap();
|
|
312
|
+
this.paramElements = new WeakMap();
|
|
294
313
|
this.nextRequestId = 1;
|
|
295
314
|
this.pending = new Map();
|
|
296
315
|
this.delegateListeners = new Set();
|
|
@@ -580,10 +599,10 @@ export class OndaAudioProcessor {
|
|
|
580
599
|
if (!Array.isArray(this.paramInfo)) {
|
|
581
600
|
return this.request("set-param", { param, value });
|
|
582
601
|
}
|
|
583
|
-
const info = paramInfoFor(this.paramInfo ?? [], param);
|
|
602
|
+
const info = paramInfoFor(this.paramInfo ?? [], param, this.paramElements);
|
|
584
603
|
return this.request("set-param", {
|
|
585
604
|
param,
|
|
586
|
-
value: constrainParamValue(info, value, this.paramControls),
|
|
605
|
+
value: constrainParamValue(info, value, this.paramControls, this.paramElements),
|
|
587
606
|
});
|
|
588
607
|
} catch (error) {
|
|
589
608
|
return Promise.reject(error);
|
|
@@ -597,7 +616,7 @@ export class OndaAudioProcessor {
|
|
|
597
616
|
"setParamNormalized requires processor metadata; construct the adapter with createOndaAudioProcessor()",
|
|
598
617
|
);
|
|
599
618
|
}
|
|
600
|
-
const info = paramInfoFor(this.paramInfo, param);
|
|
619
|
+
const info = paramInfoFor(this.paramInfo, param, this.paramElements);
|
|
601
620
|
const control = preparedParamControl(info, this.paramControls);
|
|
602
621
|
if (!control) {
|
|
603
622
|
throw new Error(`Onda parameter '${info.name}' has no scalar host-control domain`);
|
|
@@ -612,7 +631,16 @@ export class OndaAudioProcessor {
|
|
|
612
631
|
}
|
|
613
632
|
|
|
614
633
|
trigger(event, values = {}) {
|
|
615
|
-
|
|
634
|
+
try {
|
|
635
|
+
this.assertOpen();
|
|
636
|
+
const events = this.metadata?.metadata?.events;
|
|
637
|
+
const info = Number.isInteger(event) ? events?.[event] : events?.find((entry) => entry.name === event);
|
|
638
|
+
if (!info) throw new Error(`unknown Onda event '${String(event)}'`);
|
|
639
|
+
let plan = this.eventPlans.get(info);
|
|
640
|
+
if (!plan) { plan = new PayloadPlan(info.schema); this.eventPlans.set(info, plan); }
|
|
641
|
+
const payload = plan.encode(values);
|
|
642
|
+
return this.request("event", { event, payload }, [payload.buffer]);
|
|
643
|
+
} catch (error) { return Promise.reject(error); }
|
|
616
644
|
}
|
|
617
645
|
|
|
618
646
|
onDelegates(listener) {
|
|
@@ -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,
|
|
@@ -11,6 +12,7 @@ const ONDA_PROCESS_BEGIN_BLOCK = 1 << 0;
|
|
|
11
12
|
const ONDA_PROCESS_END_BLOCK = 1 << 1;
|
|
12
13
|
const ONDA_INIT_PRESERVE_PINNED = 0;
|
|
13
14
|
const ONDA_INIT_FULL = 1;
|
|
15
|
+
const PROCESSOR_EXECUTION_INPUT_REJECTED = 2;
|
|
14
16
|
const ONDA_AUDIO_WORKLET_PROCESSOR_NAME = "onda-wasm-processor";
|
|
15
17
|
const DEFAULT_EVENT_PAYLOAD_CAPACITY_BYTES = 64 * 1024;
|
|
16
18
|
const DEFAULT_DELEGATE_CAPACITY_BYTES = 64 * 1024;
|
|
@@ -188,6 +190,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
188
190
|
this.eventPayloadPtr = this.eventPayloadCapacity
|
|
189
191
|
? this.alloc(this.eventPayloadCapacity, 8)
|
|
190
192
|
: 0;
|
|
193
|
+
// Each tensor and dynamic prefix adds at most seven alignment bytes.
|
|
194
|
+
// Reserve before rendering; dispatch never grows memory for preparation.
|
|
195
|
+
this.eventWorkspaceCapacity = this.eventPayloadCapacity + 7 * Math.max(0,
|
|
196
|
+
...this.eventInfo.map((event) => event.params.length * 2));
|
|
197
|
+
this.eventWorkspacePtr = this.eventWorkspaceCapacity ? this.alloc(this.eventWorkspaceCapacity, 8) : 0;
|
|
198
|
+
this.eventInputPtr = this.eventInfo.length ? this.alloc(16, 4) : 0;
|
|
191
199
|
this.delegateCapacity = this.configuredDelegateCapacity(
|
|
192
200
|
processorOptions.delegateCapacityBytes,
|
|
193
201
|
);
|
|
@@ -340,6 +348,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
340
348
|
this.outputCapacityFrames,
|
|
341
349
|
);
|
|
342
350
|
});
|
|
351
|
+
this.eventPayloadView = new Uint8Array(buffer, this.eventPayloadPtr, this.eventPayloadCapacity);
|
|
343
352
|
this.delegateRecordView = this.delegateStoragePtr
|
|
344
353
|
? new Uint8Array(buffer, this.delegateStoragePtr, this.delegateCapacity)
|
|
345
354
|
: null;
|
|
@@ -477,13 +486,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
477
486
|
}
|
|
478
487
|
|
|
479
488
|
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
|
-
}
|
|
489
|
+
const { info: param, element } = paramAddress(this.paramInfo, selector);
|
|
487
490
|
if (value === undefined) {
|
|
488
491
|
throw new Error(`Onda parameter '${param.name}' requires a value`);
|
|
489
492
|
}
|
|
@@ -502,6 +505,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
502
505
|
) {
|
|
503
506
|
throw new Error(`Onda parameter '${param.name}' has invalid storage metadata`);
|
|
504
507
|
}
|
|
508
|
+
if (element !== null) {
|
|
509
|
+
this.writeScalar(this.paramsPtr + offset + element * this.scalarByteSize(param.scalar), param.scalar, value, this.memoryView());
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
505
512
|
this.writeStorage(
|
|
506
513
|
this.paramsPtr + offset,
|
|
507
514
|
param,
|
|
@@ -711,7 +718,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
711
718
|
} else if (message.type === "event") {
|
|
712
719
|
this.dispatchEvent(
|
|
713
720
|
message.event ?? message.name ?? message.index,
|
|
714
|
-
message.
|
|
721
|
+
message.payload,
|
|
715
722
|
);
|
|
716
723
|
this.postResponse(message, { type: "onda-ok", operation: message.type });
|
|
717
724
|
} else if (message.type === "read-control-outputs") {
|
|
@@ -753,7 +760,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
753
760
|
}
|
|
754
761
|
}
|
|
755
762
|
|
|
756
|
-
dispatchEvent(selector,
|
|
763
|
+
dispatchEvent(selector, payload) {
|
|
757
764
|
this.requireInitialized("event dispatch");
|
|
758
765
|
const eventId = Number.isInteger(selector)
|
|
759
766
|
? selector
|
|
@@ -762,61 +769,23 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
762
769
|
if (!event) {
|
|
763
770
|
throw new Error(`unknown Onda event '${String(selector)}'`);
|
|
764
771
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
const param = event.params[paramId];
|
|
768
|
-
const value = this.eventValue(event, param, paramId, values);
|
|
769
|
-
if (param.is_slice) {
|
|
770
|
-
const length = this.sequenceLength(
|
|
771
|
-
value,
|
|
772
|
-
`event '${event.name}' slice '${param.name}'`,
|
|
773
|
-
);
|
|
774
|
-
payloadSize = this.checkedEventPayloadSize(
|
|
775
|
-
payloadSize,
|
|
776
|
-
4 + length * this.scalarByteSize(param.scalar),
|
|
777
|
-
event.name,
|
|
778
|
-
);
|
|
779
|
-
} else {
|
|
780
|
-
this.validateStorageValue(param, value);
|
|
781
|
-
payloadSize = this.checkedEventPayloadSize(
|
|
782
|
-
payloadSize,
|
|
783
|
-
Number(param.byte_size ?? 0),
|
|
784
|
-
event.name,
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
772
|
+
if (!(payload instanceof Uint8Array)) throw new TypeError("event payload must be encoded bytes");
|
|
773
|
+
const payloadSize = payload.byteLength;
|
|
788
774
|
if (payloadSize > this.eventPayloadCapacity) {
|
|
789
|
-
throw new Error(
|
|
790
|
-
`event '${event.name}' requires ${payloadSize} payload bytes; configured capacity is ${this.eventPayloadCapacity}`,
|
|
791
|
-
);
|
|
775
|
+
throw new Error(`event '${event.name}' requires ${payloadSize} payload bytes; configured capacity is ${this.eventPayloadCapacity}`);
|
|
792
776
|
}
|
|
793
|
-
|
|
794
|
-
let offset = 0;
|
|
795
777
|
const view = this.memoryView();
|
|
796
|
-
|
|
797
|
-
const param = event.params[paramId];
|
|
798
|
-
const value = this.eventValue(event, param, paramId, values);
|
|
799
|
-
const address = this.eventPayloadPtr + offset;
|
|
800
|
-
if (param.is_slice) {
|
|
801
|
-
const length = this.sequenceLength(
|
|
802
|
-
value,
|
|
803
|
-
`event '${event.name}' slice '${param.name}'`,
|
|
804
|
-
);
|
|
805
|
-
view.setInt32(address, length, true);
|
|
806
|
-
this.writeScalarValues(address + 4, param.scalar, value, length, view);
|
|
807
|
-
offset += 4 + length * this.scalarByteSize(param.scalar);
|
|
808
|
-
} else {
|
|
809
|
-
this.writeStorage(address, param, value, view);
|
|
810
|
-
offset += Number(param.byte_size ?? 0);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
778
|
+
this.eventPayloadView.set(payload);
|
|
813
779
|
const handler = this.exports[event.export];
|
|
814
780
|
if (typeof handler !== "function") {
|
|
815
781
|
throw new Error(`missing WebAssembly export '${event.export}'`);
|
|
816
782
|
}
|
|
817
|
-
this.
|
|
783
|
+
view.setUint32(this.eventInputPtr, this.eventPayloadPtr, true);
|
|
784
|
+
view.setUint32(this.eventInputPtr + 4, payloadSize, true);
|
|
785
|
+
view.setUint32(this.eventInputPtr + 8, this.eventWorkspacePtr, true);
|
|
786
|
+
view.setUint32(this.eventInputPtr + 12, this.eventWorkspaceCapacity, true);
|
|
818
787
|
const status = handler(
|
|
819
|
-
this.
|
|
788
|
+
this.eventInputPtr,
|
|
820
789
|
this.paramsPtr,
|
|
821
790
|
this.statePtr,
|
|
822
791
|
this.bufferPointersPtr,
|
|
@@ -825,6 +794,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
825
794
|
this.bufferSampleRatesPtr,
|
|
826
795
|
this.executionOutputPtr,
|
|
827
796
|
);
|
|
797
|
+
if (status === PROCESSOR_EXECUTION_INPUT_REJECTED) throw new Error(`event '${event.name}' input rejected: invalid payload or insufficient workspace`);
|
|
828
798
|
this.publishExecutionOutput(
|
|
829
799
|
EXECUTION_OPERATION_EVENT,
|
|
830
800
|
eventId,
|
|
@@ -832,34 +802,6 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
832
802
|
this.checkExecutionStatus(status, `event '${event.name}'`);
|
|
833
803
|
}
|
|
834
804
|
|
|
835
|
-
eventValue(event, param, paramId, values) {
|
|
836
|
-
const supplied = Array.isArray(values)
|
|
837
|
-
? values[paramId]
|
|
838
|
-
: values?.[param.name];
|
|
839
|
-
const value = supplied === undefined
|
|
840
|
-
? this.metadataDefaultValue(param)
|
|
841
|
-
: supplied;
|
|
842
|
-
if (value === undefined) {
|
|
843
|
-
throw new Error(
|
|
844
|
-
`event '${event.name}' requires parameter '${param.name}'`,
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
return value;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
checkedEventPayloadSize(current, additional, eventName) {
|
|
851
|
-
const next = current + additional;
|
|
852
|
-
if (
|
|
853
|
-
!Number.isSafeInteger(additional)
|
|
854
|
-
|| additional < 0
|
|
855
|
-
|| !Number.isSafeInteger(next)
|
|
856
|
-
|| next > 0x7fff_ffff
|
|
857
|
-
) {
|
|
858
|
-
throw new Error(`event '${eventName}' payload exceeds the 32-bit ABI limit`);
|
|
859
|
-
}
|
|
860
|
-
return next;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
805
|
readControlOutputs() {
|
|
864
806
|
this.requireInitialized("reading control outputs");
|
|
865
807
|
return Object.fromEntries(
|