@onda-lang/webaudio 0.8.6 → 0.8.8
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 +21 -9
- package/api.md +62 -10
- package/package.json +2 -2
- package/src/index.d.ts +27 -0
- package/src/index.js +144 -22
- package/src/param-metadata.js +15 -0
- package/src/processor-constants.js +10 -0
- package/src/worklet.js +55 -29
package/README.md
CHANGED
|
@@ -57,8 +57,13 @@ write to the worklet. Boolean normalized values use the `0.5` threshold. For exa
|
|
|
57
57
|
```js
|
|
58
58
|
await processor.setParam("cutoff", 440); // exactly 440 Hz
|
|
59
59
|
await processor.setParamNormalized("cutoff", 0.5); // midpoint in its declared control scale
|
|
60
|
+
await processor.setParamElement("offsets", 1, 12); // one fixed-array element
|
|
61
|
+
await processor.setParamElementNormalized("offsets", 1, 0.5);
|
|
60
62
|
```
|
|
61
63
|
|
|
64
|
+
The element methods also accept scalar parameters with element index zero. Indexed names such as
|
|
65
|
+
`offsets[1]` remain available through `setParam()` and `setParamNormalized()`.
|
|
66
|
+
|
|
62
67
|
The same synchronous conversion helpers are re-exported for UI display and typed entry:
|
|
63
68
|
|
|
64
69
|
```js
|
|
@@ -128,7 +133,9 @@ Fixed arrays must have their declared length. Slice lengths may vary up to the c
|
|
|
128
133
|
payload capacity. `i64` leaves accept `bigint`, exact safe integers, or decimal integer strings and
|
|
129
134
|
reject values outside the signed 64-bit range. Missing top-level arguments use declared defaults;
|
|
130
135
|
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.
|
|
136
|
+
worklet preflight leaves processor state usable and rejects the returned promise. Unknown
|
|
137
|
+
nonnegative numeric event indices are successful no-ops, matching native dispatch; unknown names
|
|
138
|
+
and negative indices reject the promise.
|
|
132
139
|
|
|
133
140
|
## Prints
|
|
134
141
|
|
|
@@ -249,23 +256,28 @@ const processor = await createOndaAudioProcessorInitialized(context, artifact, {
|
|
|
249
256
|
Hosts may instead pass a flat array in physical descriptor order or key individual physical names
|
|
250
257
|
such as `"bank[1]"`. Logical group metadata determines the contiguous slot range; no sample data is
|
|
251
258
|
copied when Onda selects a slot while processing. Initial descriptors are installed before the
|
|
252
|
-
initialized
|
|
259
|
+
initialized factory awaits full initialization, so authored top-level and proc init code can
|
|
253
260
|
preprocess the supplied samples once before rendering begins.
|
|
254
261
|
|
|
255
262
|
Artifact descriptors and module exports are validated by the shared, compiler-free
|
|
256
263
|
`@onda-lang/processor-abi` package before anything reaches the rendering thread.
|
|
257
|
-
If generated init or event code returns a nonzero execution status, the adapter
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
264
|
+
If generated init or event code returns a nonzero execution status, the adapter rejects with
|
|
265
|
+
`OndaExecutionError`; its `operation` and `status` fields preserve the raw processor result. Event
|
|
266
|
+
input rejection preserves live state. The package re-exports the `PROCESSOR_EXECUTION_*` constants
|
|
267
|
+
for direct status comparisons. Runtime-safety failure invalidates the processor, so later callbacks
|
|
268
|
+
remain silent and stateful operations are rejected until full initialization or snapshot
|
|
269
|
+
restoration succeeds. Subscribe with `onExecutionError(listener)` to receive the same typed error
|
|
270
|
+
when a render-time process call fails; the failed callback emits silence.
|
|
261
271
|
`init(ONDA_INIT_PRESERVE_PINNED)` reruns generated initialization
|
|
262
272
|
while preserving pinned roots and task continuations; `init(ONDA_INIT_FULL)` initializes the
|
|
263
273
|
complete physical state and is required before processing an instance returned by
|
|
264
|
-
`createOndaAudioProcessor`. The initialized convenience
|
|
265
|
-
|
|
274
|
+
`createOndaAudioProcessor`. The initialized convenience factory installs its response and output
|
|
275
|
+
handling before requesting full initialization, then resolves only after initialization succeeds.
|
|
276
|
+
Neither mode allocates on the successful path, and a
|
|
266
277
|
failure returns the processor to its silent pending state until full initialization or snapshot
|
|
267
278
|
restore succeeds. Suspend or disconnect playback before initialization that performs substantial
|
|
268
|
-
work.
|
|
279
|
+
work. Snapshot restoration performs its internal full initialization silently and does not deliver
|
|
280
|
+
init prints or delegates.
|
|
269
281
|
|
|
270
282
|
Dynamic event storage is also allocated before rendering. Its default capacity is 64 KiB per
|
|
271
283
|
processor with dynamic events and can be changed explicitly:
|
package/api.md
CHANGED
|
@@ -121,8 +121,10 @@ versions rather than guessing layout compatibility.
|
|
|
121
121
|
`createParamControl(metadata)` validates one scalar parameter descriptor and returns a reusable
|
|
122
122
|
`OndaPreparedParamControl`. For an array, `createParamControl(metadata, index)` prepares one element;
|
|
123
123
|
`paramElementMetadata(metadata, index)` projects its scalar descriptor, default, and storage offset.
|
|
124
|
-
The Web Audio adapter
|
|
125
|
-
`
|
|
124
|
+
The Web Audio adapter exposes `setParamElement(nameOrIndex, element, value)` and
|
|
125
|
+
`setParamElementNormalized(nameOrIndex, element, normalized)` for individual scalar or fixed-array
|
|
126
|
+
elements. Indexed names such as `offsets[1]` remain accepted by `setParam` and
|
|
127
|
+
`setParamNormalized`; exact-length arrays use `setParam("offsets", values)`.
|
|
126
128
|
`createParamDomain(domain)` does the same from already-decoded values.
|
|
127
129
|
Prepared controls expose `constrainPlain`, `normalizedToPlain`, and `plainToNormalized` methods.
|
|
128
130
|
|
|
@@ -144,7 +146,8 @@ contract.
|
|
|
144
146
|
every generated init or process entry. Generated event entries reset before input preflight, so
|
|
145
147
|
rejected input returns empty batches. After a successful generated call,
|
|
146
148
|
`readDelegateBatch` or `readPrintBatch` validates the result counters.
|
|
147
|
-
`
|
|
149
|
+
`decodeDelegateBatch` validates and decodes a complete delegate batch in one call;
|
|
150
|
+
`decodeDelegateRecords` decodes an already-isolated storage region using `metadata.delegates`.
|
|
148
151
|
`decodePrintRecords` preserves primitive types and source sites; `formatPrintRecords` and
|
|
149
152
|
`formatPrintBatch` add canonical text formatting. Both decoded record types expose a `sequence` from
|
|
150
153
|
the host-reset call-local counter; merge that call's two arrays by this field when presenting one
|
|
@@ -186,27 +189,36 @@ The package root re-exports the common artifact validation and file helpers. The
|
|
|
186
189
|
- `compileOndaProcessorModule(artifact)` validates and compiles a reusable module off the render thread.
|
|
187
190
|
- `ondaAudioWorkletNodeOptions(artifact, options?)` builds low-level node options.
|
|
188
191
|
- `createOndaAudioProcessor(context, artifact, options?)` allocates an uninitialized adapter.
|
|
189
|
-
- `createOndaAudioProcessorInitialized(context, artifact, options?)` allocates
|
|
192
|
+
- `createOndaAudioProcessorInitialized(context, artifact, options?)` allocates it, requests full
|
|
193
|
+
initialization through the status-bearing control protocol, and resolves only on success.
|
|
190
194
|
- `flattenedAudioChannelCount(ports?)` totals declared physical channels with validation.
|
|
191
195
|
|
|
192
196
|
`OndaAudioProcessorOptions` accepts initial plain parameter values, external buffers, event,
|
|
193
197
|
delegate, and print capacities, an optional construction-time `onPrint` listener, a precompiled
|
|
194
198
|
module, custom node options, and an `AudioWorkletNode` constructor. Pass `onPrint` when using the
|
|
195
|
-
initialized
|
|
199
|
+
initialized factory if initialization output must be observed; registering a listener after
|
|
196
200
|
construction cannot replay output from an execution that has already completed. The artifact sample
|
|
197
201
|
rate must equal the context sample rate and it must expose at least one audio input or output.
|
|
198
202
|
Print and delegate delivery uses a bounded `SharedArrayBuffer` ring and requires cross-origin
|
|
199
203
|
isolation in browsers; ordinary audio processing remains available when shared memory is absent.
|
|
200
204
|
Low-level callers that manually pair the returned node options with `OndaAudioProcessor` must pass
|
|
201
|
-
|
|
205
|
+
the artifact metadata as the adapter constructor's second argument and
|
|
206
|
+
`processorOptions.executionOutputRing` as its fourth argument. Event encoding and numeric
|
|
207
|
+
unknown-index classification require that metadata.
|
|
202
208
|
|
|
203
209
|
### `OndaAudioProcessor`
|
|
204
210
|
|
|
205
211
|
The adapter exposes its `node` and validated `metadata`, plus these operations:
|
|
206
212
|
|
|
207
|
-
- `setParam(nameOrIndex, plain)` and `setParamNormalized(nameOrIndex, normalized)
|
|
208
|
-
|
|
209
|
-
- `
|
|
213
|
+
- `setParam(nameOrIndex, plain)` and `setParamNormalized(nameOrIndex, normalized)` for whole
|
|
214
|
+
parameters.
|
|
215
|
+
- `setParamElement(nameOrIndex, element, plain)` and
|
|
216
|
+
`setParamElementNormalized(nameOrIndex, element, normalized)` for one scalar or fixed-array
|
|
217
|
+
element.
|
|
218
|
+
- `trigger(nameOrIndex, values?)` for input events. Unknown nonnegative numeric indices are
|
|
219
|
+
successful no-ops, matching native dispatch; unknown names and negative indices are rejected.
|
|
220
|
+
- `onExecutionError(listener)`, `onDelegates(listener)`, and `onPrint(listener)`, each returning an
|
|
221
|
+
unsubscribe function.
|
|
210
222
|
- `init(mode)`, `snapshot()`, and `restoreSnapshot(bytes)`.
|
|
211
223
|
- `readControlOutputs()` and `readBuffer(nameOrIndex)`.
|
|
212
224
|
- `request(type, fields?, transfer?)` for adapter protocol extensions.
|
|
@@ -224,7 +236,24 @@ each init, event, or process segment.
|
|
|
224
236
|
|
|
225
237
|
`ONDA_INIT_FULL` clears and initializes all physical state. `ONDA_INIT_PRESERVE_PINNED` retains
|
|
226
238
|
pinned state according to the processor ABI. The package also re-exports the prepared and one-shot
|
|
227
|
-
parameter conversion helpers.
|
|
239
|
+
parameter conversion helpers. Snapshot restoration performs its internal full initialization
|
|
240
|
+
silently, without delivering init prints or delegates.
|
|
241
|
+
|
|
242
|
+
Raw WebAssembly exports use the same generated status values as native processor objects:
|
|
243
|
+
`PROCESSOR_EXECUTION_OK` (`0`), `PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE` (`1`), and event-only
|
|
244
|
+
`PROCESSOR_EXECUTION_INPUT_REJECTED` (`2`). The Web Audio adapter translates host-side validation
|
|
245
|
+
problems into rejected promises before generated execution. It also rejects init and event promises
|
|
246
|
+
when generated code returns a nonzero status: status `1` invalidates the live processor, while event
|
|
247
|
+
status `2` preserves it. These generated failures use `OndaExecutionError`, whose `operation` and
|
|
248
|
+
`status` fields retain the machine-readable failure. The Web Audio root re-exports all three
|
|
249
|
+
`PROCESSOR_EXECUTION_*` constants for direct comparison. Render-time process failure invalidates the
|
|
250
|
+
processor and silences subsequent callbacks. `onExecutionError(listener)` receives its typed
|
|
251
|
+
`OndaExecutionError`; requested init and event failures remain on their promise rejection path and
|
|
252
|
+
are not reported twice.
|
|
253
|
+
The raw `@onda-lang/processor-abi` package exposes `PROCESSOR_BEGIN_BLOCK`,
|
|
254
|
+
`PROCESSOR_END_BLOCK`, and `PROCESSOR_FULL_BLOCK` for native and direct WebAssembly hosts that use
|
|
255
|
+
segmented processing. Web Audio schedules render quanta internally and has no segmented-process
|
|
256
|
+
method.
|
|
228
257
|
|
|
229
258
|
`@onda-lang/webaudio/worklet` is the side-effect-only AudioWorklet registration module and has no
|
|
230
259
|
named exports.
|
|
@@ -271,8 +300,11 @@ PRINT_RECORD_HEADER_SIZE_BYTES
|
|
|
271
300
|
PROCESSOR_ABI_VERSION
|
|
272
301
|
PROCESSOR_ARTIFACT_FORMAT
|
|
273
302
|
PROCESSOR_ARTIFACT_FORMAT_VERSION
|
|
303
|
+
PROCESSOR_BEGIN_BLOCK
|
|
304
|
+
PROCESSOR_END_BLOCK
|
|
274
305
|
PROCESSOR_EXECUTION_OK
|
|
275
306
|
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE
|
|
307
|
+
PROCESSOR_FULL_BLOCK
|
|
276
308
|
PROCESSOR_INIT_FULL
|
|
277
309
|
PROCESSOR_INIT_PRESERVE_PINNED
|
|
278
310
|
PROCESSOR_SNAPSHOT_FORMAT_VERSION
|
|
@@ -281,6 +313,7 @@ createParamControl
|
|
|
281
313
|
paramElementMetadata
|
|
282
314
|
createParamDomain
|
|
283
315
|
createProcessorArtifactFiles
|
|
316
|
+
decodeDelegateBatch
|
|
284
317
|
decodeDelegateRecords
|
|
285
318
|
decodePrintRecords
|
|
286
319
|
formatPrintBatch
|
|
@@ -320,12 +353,19 @@ OndaArtifactError
|
|
|
320
353
|
OndaBinaryenError
|
|
321
354
|
OndaBinaryenOptions
|
|
322
355
|
OndaProcessorArtifact
|
|
356
|
+
OndaProcessorInitMode
|
|
323
357
|
OndaProcessorMetadata
|
|
324
358
|
PROCESSOR_ABI_VERSION
|
|
325
359
|
PROCESSOR_ARTIFACT_FORMAT
|
|
326
360
|
PROCESSOR_ARTIFACT_FORMAT_VERSION
|
|
361
|
+
PROCESSOR_BEGIN_BLOCK
|
|
362
|
+
PROCESSOR_END_BLOCK
|
|
363
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED
|
|
327
364
|
PROCESSOR_EXECUTION_OK
|
|
328
365
|
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE
|
|
366
|
+
PROCESSOR_FULL_BLOCK
|
|
367
|
+
PROCESSOR_INIT_FULL
|
|
368
|
+
PROCESSOR_INIT_PRESERVE_PINNED
|
|
329
369
|
PROCESSOR_SNAPSHOT_FORMAT_VERSION
|
|
330
370
|
SUPPORTED_MIR_SCHEMA_VERSION
|
|
331
371
|
compileTrustedMir
|
|
@@ -366,6 +406,7 @@ OndaLspAnalysisOptions
|
|
|
366
406
|
OndaLspMessage
|
|
367
407
|
OndaMaterializedProjectFile
|
|
368
408
|
OndaProcessorArtifact
|
|
409
|
+
OndaProcessorInitMode
|
|
369
410
|
OndaProcessorMetadata
|
|
370
411
|
OndaProjectBufferInfo
|
|
371
412
|
OndaProjectCapabilities
|
|
@@ -383,8 +424,14 @@ OndaWorkerLike
|
|
|
383
424
|
PROCESSOR_ABI_VERSION
|
|
384
425
|
PROCESSOR_ARTIFACT_FORMAT
|
|
385
426
|
PROCESSOR_ARTIFACT_FORMAT_VERSION
|
|
427
|
+
PROCESSOR_BEGIN_BLOCK
|
|
428
|
+
PROCESSOR_END_BLOCK
|
|
429
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED
|
|
386
430
|
PROCESSOR_EXECUTION_OK
|
|
387
431
|
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE
|
|
432
|
+
PROCESSOR_FULL_BLOCK
|
|
433
|
+
PROCESSOR_INIT_FULL
|
|
434
|
+
PROCESSOR_INIT_PRESERVE_PINNED
|
|
388
435
|
PROCESSOR_SNAPSHOT_FORMAT_VERSION
|
|
389
436
|
WorkerCompilerOptions
|
|
390
437
|
createCompiler
|
|
@@ -408,11 +455,16 @@ OndaAudioProcessor
|
|
|
408
455
|
OndaAudioProcessorOptions
|
|
409
456
|
OndaAudioPrintBatch
|
|
410
457
|
OndaAudioPrintListener
|
|
458
|
+
OndaExecutionError
|
|
459
|
+
OndaExecutionErrorListener
|
|
411
460
|
OndaInitMode
|
|
412
461
|
OndaParamDomain
|
|
413
462
|
OndaPreparedParamControl
|
|
414
463
|
OndaProcessorArtifact
|
|
415
464
|
OndaProcessorMetadata
|
|
465
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED
|
|
466
|
+
PROCESSOR_EXECUTION_OK
|
|
467
|
+
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE
|
|
416
468
|
compileOndaProcessorModule
|
|
417
469
|
constrainParamPlain
|
|
418
470
|
createOndaAudioProcessor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onda-lang/webaudio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
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.8"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
package/src/index.d.ts
CHANGED
|
@@ -3,6 +3,21 @@ export const ONDA_INIT_PRESERVE_PINNED: 0;
|
|
|
3
3
|
export const ONDA_INIT_FULL: 1;
|
|
4
4
|
export type OndaInitMode = 0 | 1;
|
|
5
5
|
|
|
6
|
+
export {
|
|
7
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED,
|
|
8
|
+
PROCESSOR_EXECUTION_OK,
|
|
9
|
+
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
|
|
10
|
+
} from "@onda-lang/processor-abi";
|
|
11
|
+
|
|
12
|
+
/** A nonzero status returned by generated processor execution. */
|
|
13
|
+
export class OndaExecutionError extends Error {
|
|
14
|
+
constructor(message: string, operation: string, status: number);
|
|
15
|
+
readonly operation: string;
|
|
16
|
+
readonly status: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type OndaExecutionErrorListener = (error: OndaExecutionError) => void;
|
|
20
|
+
|
|
6
21
|
export type {
|
|
7
22
|
OndaParamDomain,
|
|
8
23
|
OndaPreparedParamControl,
|
|
@@ -88,6 +103,7 @@ export function createOndaAudioProcessor(
|
|
|
88
103
|
artifact: OndaProcessorArtifact,
|
|
89
104
|
options?: OndaAudioProcessorOptions,
|
|
90
105
|
): Promise<OndaAudioProcessor>;
|
|
106
|
+
/** Resolves after full initialization succeeds; generated failures reject with OndaExecutionError. */
|
|
91
107
|
export function createOndaAudioProcessorInitialized(
|
|
92
108
|
context: BaseAudioContext,
|
|
93
109
|
artifact: OndaProcessorArtifact,
|
|
@@ -110,9 +126,20 @@ export class OndaAudioProcessor {
|
|
|
110
126
|
request(type: string, fields?: Record<string, unknown>, transfer?: Transferable[]): Promise<any>;
|
|
111
127
|
/** Set a plain Onda value; ranged scalar values are clamped and snapped. */
|
|
112
128
|
setParam(param: string | number, value: unknown): Promise<any>;
|
|
129
|
+
/** Set one scalar or fixed-array parameter element without modifying its siblings. */
|
|
130
|
+
setParamElement(param: string | number, element: number, value: unknown): Promise<any>;
|
|
113
131
|
/** Map a host value in [0, 1] through the descriptor and set the resulting plain value. */
|
|
114
132
|
setParamNormalized(param: string | number, value: number): Promise<any>;
|
|
133
|
+
/** Normalized form of setParamElement(). */
|
|
134
|
+
setParamElementNormalized(
|
|
135
|
+
param: string | number,
|
|
136
|
+
element: number,
|
|
137
|
+
value: number,
|
|
138
|
+
): Promise<any>;
|
|
139
|
+
/** Unknown nonnegative numeric event indices are successful no-ops; unknown names are rejected. */
|
|
115
140
|
trigger(event: string | number, values?: Record<string, unknown> | unknown[]): Promise<any>;
|
|
141
|
+
/** Subscribe to asynchronous render-time generated failures. */
|
|
142
|
+
onExecutionError(listener: OndaExecutionErrorListener): () => boolean;
|
|
116
143
|
/**
|
|
117
144
|
* Subscribe to batches decoded after generated execution. Collection is active only while at
|
|
118
145
|
* least one listener is registered. overflowCount reports configured-capacity loss and
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { paramAddress } from "./param-metadata.js";
|
|
1
|
+
import { paramAddress, paramElementAddress } from "./param-metadata.js";
|
|
2
2
|
import {
|
|
3
3
|
PayloadPlan,
|
|
4
4
|
createParamControl,
|
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
drainExecutionOutputRing,
|
|
19
19
|
openExecutionOutputRing,
|
|
20
20
|
} from "./execution-output-ring.js";
|
|
21
|
+
import {
|
|
22
|
+
PROCESSOR_INIT_FULL,
|
|
23
|
+
PROCESSOR_INIT_PRESERVE_PINNED,
|
|
24
|
+
} from "./processor-constants.js";
|
|
21
25
|
|
|
22
26
|
export {
|
|
23
27
|
createParamDomain,
|
|
@@ -25,11 +29,23 @@ export {
|
|
|
25
29
|
constrainParamPlain,
|
|
26
30
|
paramNormalizedToPlain,
|
|
27
31
|
paramPlainToNormalized,
|
|
32
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED,
|
|
33
|
+
PROCESSOR_EXECUTION_OK,
|
|
34
|
+
PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
|
|
28
35
|
} from "@onda-lang/processor-abi";
|
|
29
36
|
|
|
30
37
|
export const ONDA_AUDIO_WORKLET_PROCESSOR_NAME = "onda-wasm-processor";
|
|
31
|
-
export const ONDA_INIT_PRESERVE_PINNED =
|
|
32
|
-
export const ONDA_INIT_FULL =
|
|
38
|
+
export const ONDA_INIT_PRESERVE_PINNED = PROCESSOR_INIT_PRESERVE_PINNED;
|
|
39
|
+
export const ONDA_INIT_FULL = PROCESSOR_INIT_FULL;
|
|
40
|
+
|
|
41
|
+
export class OndaExecutionError extends Error {
|
|
42
|
+
constructor(message, operation, status) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "OndaExecutionError";
|
|
45
|
+
this.operation = operation;
|
|
46
|
+
this.status = status;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
33
49
|
|
|
34
50
|
const registrationByContext = new WeakMap();
|
|
35
51
|
const DEFAULT_DELEGATE_CAPACITY_BYTES = 64 * 1024;
|
|
@@ -171,6 +187,11 @@ function paramInfoFor(params, selector, cache = null) {
|
|
|
171
187
|
return element === null ? info : paramElementInfo(info, element, cache);
|
|
172
188
|
}
|
|
173
189
|
|
|
190
|
+
function paramElementInfoFor(params, selector, element, cache = null) {
|
|
191
|
+
const address = paramElementAddress(params, selector, element);
|
|
192
|
+
return paramElementInfo(address.info, address.element, cache);
|
|
193
|
+
}
|
|
194
|
+
|
|
174
195
|
function preparedParamControl(info, cache = null) {
|
|
175
196
|
if (info.type_repr !== info.scalar) return null;
|
|
176
197
|
if (info.scalar !== "bool" && info.param_control === null) return null;
|
|
@@ -263,7 +284,7 @@ async function createOndaAudioProcessorImpl(context, artifact, options, initiali
|
|
|
263
284
|
}
|
|
264
285
|
const nodeOptions = audioWorkletNodeOptionsFromValidated(
|
|
265
286
|
validated,
|
|
266
|
-
{ ...options, compiledModule, initialize },
|
|
287
|
+
{ ...options, compiledModule, initialize: false },
|
|
267
288
|
false,
|
|
268
289
|
);
|
|
269
290
|
const node = new NodeConstructor(
|
|
@@ -271,12 +292,32 @@ async function createOndaAudioProcessorImpl(context, artifact, options, initiali
|
|
|
271
292
|
ONDA_AUDIO_WORKLET_PROCESSOR_NAME,
|
|
272
293
|
nodeOptions,
|
|
273
294
|
);
|
|
274
|
-
|
|
295
|
+
const processor = new OndaAudioProcessor(
|
|
275
296
|
node,
|
|
276
297
|
validated.metadata,
|
|
277
298
|
options.onPrint,
|
|
278
299
|
nodeOptions.processorOptions.executionOutputRing,
|
|
279
300
|
);
|
|
301
|
+
if (!initialize) return processor;
|
|
302
|
+
try {
|
|
303
|
+
await processor.init(ONDA_INIT_FULL);
|
|
304
|
+
return processor;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
// Initialization publishes diagnostic prints before reporting its status. Consume any
|
|
307
|
+
// already-published records while the construction-time listener is still installed.
|
|
308
|
+
try {
|
|
309
|
+
processor.drainExecutionOutput();
|
|
310
|
+
} catch {
|
|
311
|
+
// Preserve the initialization failure if consuming its diagnostics also fails.
|
|
312
|
+
}
|
|
313
|
+
processor.close(error);
|
|
314
|
+
try {
|
|
315
|
+
node.disconnect?.();
|
|
316
|
+
} catch {
|
|
317
|
+
// Preserve the initialization failure if node teardown also fails.
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
280
321
|
}
|
|
281
322
|
|
|
282
323
|
export async function compileOndaProcessorModule(artifact) {
|
|
@@ -312,6 +353,7 @@ export class OndaAudioProcessor {
|
|
|
312
353
|
this.paramElements = new WeakMap();
|
|
313
354
|
this.nextRequestId = 1;
|
|
314
355
|
this.pending = new Map();
|
|
356
|
+
this.executionErrorListeners = new Set();
|
|
315
357
|
this.delegateListeners = new Set();
|
|
316
358
|
this.delegateSubscriptionId = 0;
|
|
317
359
|
this.printListeners = new Set(
|
|
@@ -333,15 +375,22 @@ export class OndaAudioProcessor {
|
|
|
333
375
|
this.closeReason = null;
|
|
334
376
|
this.handleMessage = (event) => {
|
|
335
377
|
const message = event.data ?? {};
|
|
336
|
-
|
|
378
|
+
const error = message.type === "onda-error"
|
|
379
|
+
? Number.isInteger(message.status)
|
|
380
|
+
? new OndaExecutionError(message.error, message.operation, message.status)
|
|
381
|
+
: new Error(message.error)
|
|
382
|
+
: null;
|
|
383
|
+
if (message.requestId === undefined) {
|
|
384
|
+
if (error instanceof OndaExecutionError) {
|
|
385
|
+
this.notifyListeners(this.executionErrorListeners, error);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
337
389
|
const pending = this.pending.get(message.requestId);
|
|
338
390
|
if (!pending) return;
|
|
339
391
|
this.pending.delete(message.requestId);
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
} else {
|
|
343
|
-
pending.resolve(message);
|
|
344
|
-
}
|
|
392
|
+
if (error) pending.reject(error);
|
|
393
|
+
else pending.resolve(message);
|
|
345
394
|
};
|
|
346
395
|
node.port.addEventListener("message", this.handleMessage);
|
|
347
396
|
node.port.start?.();
|
|
@@ -375,7 +424,7 @@ export class OndaAudioProcessor {
|
|
|
375
424
|
if (result.async) await result.value;
|
|
376
425
|
}
|
|
377
426
|
};
|
|
378
|
-
void wait().catch((error) => this.
|
|
427
|
+
void wait().catch((error) => this.reportListenerError(error));
|
|
379
428
|
}
|
|
380
429
|
|
|
381
430
|
stopExecutionOutputDrain() {
|
|
@@ -398,18 +447,18 @@ export class OndaAudioProcessor {
|
|
|
398
447
|
);
|
|
399
448
|
}
|
|
400
449
|
|
|
401
|
-
|
|
450
|
+
reportListenerError(error) {
|
|
402
451
|
queueMicrotask(() => {
|
|
403
452
|
throw error;
|
|
404
453
|
});
|
|
405
454
|
}
|
|
406
455
|
|
|
407
|
-
|
|
456
|
+
notifyListeners(listeners, value) {
|
|
408
457
|
for (const listener of listeners) {
|
|
409
458
|
try {
|
|
410
|
-
listener(
|
|
459
|
+
listener(value);
|
|
411
460
|
} catch (error) {
|
|
412
|
-
this.
|
|
461
|
+
this.reportListenerError(error);
|
|
413
462
|
}
|
|
414
463
|
}
|
|
415
464
|
}
|
|
@@ -422,7 +471,7 @@ export class OndaAudioProcessor {
|
|
|
422
471
|
try {
|
|
423
472
|
this.consumeExecutionOutput(entry);
|
|
424
473
|
} catch (error) {
|
|
425
|
-
this.
|
|
474
|
+
this.reportListenerError(error);
|
|
426
475
|
}
|
|
427
476
|
},
|
|
428
477
|
);
|
|
@@ -540,7 +589,7 @@ export class OndaAudioProcessor {
|
|
|
540
589
|
entries: batchRecords.map((record) => record.entry),
|
|
541
590
|
...meta,
|
|
542
591
|
};
|
|
543
|
-
this.
|
|
592
|
+
this.notifyListeners(this.printListeners, batch);
|
|
544
593
|
} else {
|
|
545
594
|
const meta = delegate ?? { overflowCount: 0, transportDropCount: 0 };
|
|
546
595
|
delegate = null;
|
|
@@ -550,7 +599,7 @@ export class OndaAudioProcessor {
|
|
|
550
599
|
occurrences: batchRecords.map((record) => record.occurrence),
|
|
551
600
|
...meta,
|
|
552
601
|
};
|
|
553
|
-
this.
|
|
602
|
+
this.notifyListeners(this.delegateListeners, batch);
|
|
554
603
|
}
|
|
555
604
|
cursor = end;
|
|
556
605
|
}
|
|
@@ -562,7 +611,7 @@ export class OndaAudioProcessor {
|
|
|
562
611
|
entries: [],
|
|
563
612
|
...print,
|
|
564
613
|
};
|
|
565
|
-
this.
|
|
614
|
+
this.notifyListeners(this.printListeners, batch);
|
|
566
615
|
}
|
|
567
616
|
if (
|
|
568
617
|
delegate
|
|
@@ -574,7 +623,7 @@ export class OndaAudioProcessor {
|
|
|
574
623
|
occurrences: [],
|
|
575
624
|
...delegate,
|
|
576
625
|
};
|
|
577
|
-
this.
|
|
626
|
+
this.notifyListeners(this.delegateListeners, batch);
|
|
578
627
|
}
|
|
579
628
|
}
|
|
580
629
|
|
|
@@ -609,6 +658,29 @@ export class OndaAudioProcessor {
|
|
|
609
658
|
}
|
|
610
659
|
}
|
|
611
660
|
|
|
661
|
+
setParamElement(param, element, value) {
|
|
662
|
+
try {
|
|
663
|
+
if (!Array.isArray(this.paramInfo)) {
|
|
664
|
+
throw new Error(
|
|
665
|
+
"setParamElement requires processor metadata; construct the adapter with createOndaAudioProcessor()",
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
const info = paramElementInfoFor(
|
|
669
|
+
this.paramInfo,
|
|
670
|
+
param,
|
|
671
|
+
element,
|
|
672
|
+
this.paramElements,
|
|
673
|
+
);
|
|
674
|
+
return this.request("set-param", {
|
|
675
|
+
param,
|
|
676
|
+
element,
|
|
677
|
+
value: constrainParamValue(info, value, this.paramControls, this.paramElements),
|
|
678
|
+
});
|
|
679
|
+
} catch (error) {
|
|
680
|
+
return Promise.reject(error);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
612
684
|
setParamNormalized(param, value) {
|
|
613
685
|
try {
|
|
614
686
|
if (!Array.isArray(this.paramInfo)) {
|
|
@@ -630,11 +702,51 @@ export class OndaAudioProcessor {
|
|
|
630
702
|
}
|
|
631
703
|
}
|
|
632
704
|
|
|
705
|
+
setParamElementNormalized(param, element, value) {
|
|
706
|
+
try {
|
|
707
|
+
if (!Array.isArray(this.paramInfo)) {
|
|
708
|
+
throw new Error(
|
|
709
|
+
"setParamElementNormalized requires processor metadata; construct the adapter with createOndaAudioProcessor()",
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
const info = paramElementInfoFor(
|
|
713
|
+
this.paramInfo,
|
|
714
|
+
param,
|
|
715
|
+
element,
|
|
716
|
+
this.paramElements,
|
|
717
|
+
);
|
|
718
|
+
const control = preparedParamControl(info, this.paramControls);
|
|
719
|
+
if (!control) {
|
|
720
|
+
throw new Error(`Onda parameter '${info.name}' has no scalar host-control domain`);
|
|
721
|
+
}
|
|
722
|
+
return this.request("set-param", {
|
|
723
|
+
param,
|
|
724
|
+
element,
|
|
725
|
+
value: control.normalizedToPlain(value),
|
|
726
|
+
});
|
|
727
|
+
} catch (error) {
|
|
728
|
+
return Promise.reject(error);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
633
732
|
trigger(event, values = {}) {
|
|
634
733
|
try {
|
|
635
734
|
this.assertOpen();
|
|
636
735
|
const events = this.metadata?.metadata?.events;
|
|
637
|
-
|
|
736
|
+
if (Number.isInteger(event)) {
|
|
737
|
+
if (!Array.isArray(events)) {
|
|
738
|
+
throw new Error(
|
|
739
|
+
"event triggers require processor metadata; construct the adapter with createOndaAudioProcessor()",
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
if (event >= events.length) {
|
|
743
|
+
const payload = new Uint8Array();
|
|
744
|
+
return this.request("event", { event, payload }, [payload.buffer]);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const info = Number.isInteger(event)
|
|
748
|
+
? events?.[event]
|
|
749
|
+
: events?.find((entry) => entry.name === event);
|
|
638
750
|
if (!info) throw new Error(`unknown Onda event '${String(event)}'`);
|
|
639
751
|
let plan = this.eventPlans.get(info);
|
|
640
752
|
if (!plan) { plan = new PayloadPlan(info.schema); this.eventPlans.set(info, plan); }
|
|
@@ -643,6 +755,15 @@ export class OndaAudioProcessor {
|
|
|
643
755
|
} catch (error) { return Promise.reject(error); }
|
|
644
756
|
}
|
|
645
757
|
|
|
758
|
+
onExecutionError(listener) {
|
|
759
|
+
this.assertOpen();
|
|
760
|
+
if (typeof listener !== "function") {
|
|
761
|
+
throw new TypeError("execution-error listener must be a function");
|
|
762
|
+
}
|
|
763
|
+
this.executionErrorListeners.add(listener);
|
|
764
|
+
return () => this.executionErrorListeners.delete(listener);
|
|
765
|
+
}
|
|
766
|
+
|
|
646
767
|
onDelegates(listener) {
|
|
647
768
|
this.assertOpen();
|
|
648
769
|
if (typeof listener !== "function") {
|
|
@@ -775,6 +896,7 @@ export class OndaAudioProcessor {
|
|
|
775
896
|
this.stopExecutionOutputDrain();
|
|
776
897
|
for (const pending of this.pending.values()) pending.reject(this.closeReason);
|
|
777
898
|
this.pending.clear();
|
|
899
|
+
this.executionErrorListeners.clear();
|
|
778
900
|
this.delegateListeners.clear();
|
|
779
901
|
this.printListeners.clear();
|
|
780
902
|
}
|
package/src/param-metadata.js
CHANGED
|
@@ -16,3 +16,18 @@ export function paramAddress(params, selector) {
|
|
|
16
16
|
}
|
|
17
17
|
throw new Error(`unknown Onda parameter '${String(selector)}'`);
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
export function paramElementAddress(params, selector, element) {
|
|
21
|
+
const address = paramAddress(params, selector);
|
|
22
|
+
if (address.element !== null) {
|
|
23
|
+
throw new Error("an explicit element write requires a parameter name or index");
|
|
24
|
+
}
|
|
25
|
+
if (
|
|
26
|
+
!Number.isSafeInteger(element)
|
|
27
|
+
|| element < 0
|
|
28
|
+
|| element >= address.info.array_len
|
|
29
|
+
) {
|
|
30
|
+
throw new Error(`Onda parameter '${address.info.name}' element index is out of bounds`);
|
|
31
|
+
}
|
|
32
|
+
return { info: address.info, element };
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Keep the AudioWorklet dependency graph relative-only so the published module can be loaded
|
|
2
|
+
// directly. The adapter tests verify these processor ABI values against @onda-lang/processor-abi.
|
|
3
|
+
export const PROCESSOR_EXECUTION_OK = 0;
|
|
4
|
+
export const PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE = 1;
|
|
5
|
+
export const PROCESSOR_EXECUTION_INPUT_REJECTED = 2;
|
|
6
|
+
export const PROCESSOR_BEGIN_BLOCK = 1 << 0;
|
|
7
|
+
export const PROCESSOR_END_BLOCK = 1 << 1;
|
|
8
|
+
export const PROCESSOR_FULL_BLOCK = PROCESSOR_BEGIN_BLOCK | PROCESSOR_END_BLOCK;
|
|
9
|
+
export const PROCESSOR_INIT_PRESERVE_PINNED = 0;
|
|
10
|
+
export const PROCESSOR_INIT_FULL = 1;
|
package/src/worklet.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { paramAddress } from "./param-metadata.js";
|
|
1
|
+
import { paramAddress, paramElementAddress } from "./param-metadata.js";
|
|
2
2
|
import {
|
|
3
3
|
EXECUTION_OPERATION_EVENT,
|
|
4
4
|
EXECUTION_OPERATION_INIT,
|
|
@@ -7,16 +7,25 @@ import {
|
|
|
7
7
|
openExecutionOutputRing,
|
|
8
8
|
writeExecutionOutputRing,
|
|
9
9
|
} from "./execution-output-ring.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
import {
|
|
11
|
+
PROCESSOR_BEGIN_BLOCK,
|
|
12
|
+
PROCESSOR_END_BLOCK,
|
|
13
|
+
PROCESSOR_EXECUTION_OK,
|
|
14
|
+
PROCESSOR_EXECUTION_INPUT_REJECTED,
|
|
15
|
+
PROCESSOR_INIT_FULL,
|
|
16
|
+
PROCESSOR_INIT_PRESERVE_PINNED,
|
|
17
|
+
} from "./processor-constants.js";
|
|
16
18
|
const ONDA_AUDIO_WORKLET_PROCESSOR_NAME = "onda-wasm-processor";
|
|
17
19
|
const DEFAULT_EVENT_PAYLOAD_CAPACITY_BYTES = 64 * 1024;
|
|
18
20
|
const DEFAULT_DELEGATE_CAPACITY_BYTES = 64 * 1024;
|
|
19
21
|
const DEFAULT_PRINT_CAPACITY_BYTES = 64 * 1024;
|
|
22
|
+
|
|
23
|
+
class ProcessorExecutionError extends Error {
|
|
24
|
+
constructor(message, status) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.status = status;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
20
29
|
const DELEGATE_BATCH_SIZE_BYTES = 20;
|
|
21
30
|
const PRINT_BATCH_SIZE_BYTES = 20;
|
|
22
31
|
const EXECUTION_OUTPUT_SIZE_BYTES = 12;
|
|
@@ -289,7 +298,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
289
298
|
this.refreshMemoryCache(true);
|
|
290
299
|
this.invalidateState();
|
|
291
300
|
if (processorOptions.initialize === true) {
|
|
292
|
-
this.init(
|
|
301
|
+
this.init(PROCESSOR_INIT_FULL);
|
|
293
302
|
}
|
|
294
303
|
this.allocationLocked = true;
|
|
295
304
|
this.port.onmessage = (event) => this.handleMessage(event.data ?? {});
|
|
@@ -485,8 +494,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
485
494
|
}
|
|
486
495
|
}
|
|
487
496
|
|
|
488
|
-
setParam(selector, value) {
|
|
489
|
-
const { info: param, element } =
|
|
497
|
+
setParam(selector, value, explicitElement = null) {
|
|
498
|
+
const { info: param, element } = explicitElement === null
|
|
499
|
+
? paramAddress(this.paramInfo, selector)
|
|
500
|
+
: paramElementAddress(this.paramInfo, selector, explicitElement);
|
|
490
501
|
if (value === undefined) {
|
|
491
502
|
throw new Error(`Onda parameter '${param.name}' requires a value`);
|
|
492
503
|
}
|
|
@@ -517,10 +528,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
517
528
|
}
|
|
518
529
|
|
|
519
530
|
init(mode) {
|
|
520
|
-
if (mode !==
|
|
531
|
+
if (mode !== PROCESSOR_INIT_PRESERVE_PINNED && mode !== PROCESSOR_INIT_FULL) {
|
|
521
532
|
throw new Error(`invalid Onda init mode '${String(mode)}'`);
|
|
522
533
|
}
|
|
523
|
-
if (mode ===
|
|
534
|
+
if (mode === PROCESSOR_INIT_PRESERVE_PINNED && !this.initialized) {
|
|
524
535
|
throw new Error("full initialization is required before preserving pinned state");
|
|
525
536
|
}
|
|
526
537
|
this.runInitialization(mode);
|
|
@@ -538,7 +549,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
538
549
|
this.blockCursor = blockCursor;
|
|
539
550
|
}
|
|
540
551
|
|
|
541
|
-
runInitialization(mode, afterInitialize) {
|
|
552
|
+
runInitialization(mode, afterInitialize, collectOutput = true) {
|
|
542
553
|
// Reinitializing state does not create a compile-block boundary. Retain
|
|
543
554
|
// the host-side position so the next process segment cannot synthesize an
|
|
544
555
|
// extra BEGIN_BLOCK or postpone the matching END_BLOCK.
|
|
@@ -548,7 +559,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
548
559
|
// leaves the processor on the silent pending path.
|
|
549
560
|
this.invalidateState();
|
|
550
561
|
this.refreshMemoryCache();
|
|
551
|
-
this.prepareExecutionOutput();
|
|
562
|
+
if (collectOutput) this.prepareExecutionOutput();
|
|
552
563
|
const status = this.exports.onda_processor_init(
|
|
553
564
|
this.paramsPtr,
|
|
554
565
|
this.statePtr,
|
|
@@ -557,13 +568,15 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
557
568
|
this.bufferFramesPtr,
|
|
558
569
|
this.bufferChannelsPtr,
|
|
559
570
|
this.bufferSampleRatesPtr,
|
|
560
|
-
this.executionOutputPtr,
|
|
571
|
+
collectOutput ? this.executionOutputPtr : 0,
|
|
561
572
|
);
|
|
562
573
|
// Generated failures retain diagnostic prints but clear delegates.
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
574
|
+
if (collectOutput) {
|
|
575
|
+
this.publishExecutionOutput(
|
|
576
|
+
EXECUTION_OPERATION_INIT,
|
|
577
|
+
0,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
567
580
|
this.checkExecutionStatus(status, "processor init");
|
|
568
581
|
afterInitialize?.();
|
|
569
582
|
this.commitInitializedState(blockCursor);
|
|
@@ -576,9 +589,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
576
589
|
}
|
|
577
590
|
|
|
578
591
|
checkExecutionStatus(status, operation) {
|
|
579
|
-
if (status ===
|
|
592
|
+
if (status === PROCESSOR_EXECUTION_OK) return;
|
|
580
593
|
this.invalidateState();
|
|
581
|
-
throw new
|
|
594
|
+
throw new ProcessorExecutionError(
|
|
595
|
+
`${operation} failed with Onda execution status ${String(status)}`,
|
|
596
|
+
status,
|
|
597
|
+
);
|
|
582
598
|
}
|
|
583
599
|
|
|
584
600
|
createSnapshot() {
|
|
@@ -609,7 +625,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
609
625
|
// The ABI restore base is a fresh post-init image, so scratch and
|
|
610
626
|
// control-mirror state never leak across a restore. Initialization and
|
|
611
627
|
// overlay form one lifecycle transition: neither partial result is ready.
|
|
612
|
-
this.runInitialization(
|
|
628
|
+
this.runInitialization(PROCESSOR_INIT_FULL, () => {
|
|
613
629
|
const state = this.stateBytes;
|
|
614
630
|
for (const entry of this.snapshotInfo) {
|
|
615
631
|
const packedOffset = Number(entry.packed_snapshot_byte_offset);
|
|
@@ -622,7 +638,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
622
638
|
);
|
|
623
639
|
this.normalizeSnapshotIntegerRange(entry, state, physicalOffset, byteSize);
|
|
624
640
|
}
|
|
625
|
-
});
|
|
641
|
+
}, false);
|
|
626
642
|
}
|
|
627
643
|
|
|
628
644
|
normalizeSnapshotIntegerRange(entry, state, offset, byteSize) {
|
|
@@ -710,6 +726,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
710
726
|
this.setParam(
|
|
711
727
|
message.param ?? message.name ?? message.index,
|
|
712
728
|
message.value,
|
|
729
|
+
message.element ?? null,
|
|
713
730
|
);
|
|
714
731
|
this.postResponse(message, { type: "onda-ok", operation: message.type });
|
|
715
732
|
} else if (message.type === "init") {
|
|
@@ -751,17 +768,20 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
751
768
|
throw new Error(`unknown Onda worklet operation '${String(message.type)}'`);
|
|
752
769
|
}
|
|
753
770
|
} catch (error) {
|
|
754
|
-
|
|
771
|
+
const response = {
|
|
755
772
|
type: "onda-error",
|
|
756
773
|
operation: message.type ?? "unknown",
|
|
757
774
|
requestId: message.requestId,
|
|
758
775
|
error: String(error && error.message ? error.message : error),
|
|
759
|
-
}
|
|
776
|
+
};
|
|
777
|
+
if (Number.isInteger(error?.status)) response.status = error.status;
|
|
778
|
+
this.port.postMessage(response);
|
|
760
779
|
}
|
|
761
780
|
}
|
|
762
781
|
|
|
763
782
|
dispatchEvent(selector, payload) {
|
|
764
783
|
this.requireInitialized("event dispatch");
|
|
784
|
+
if (Number.isInteger(selector) && selector >= this.eventInfo.length) return;
|
|
765
785
|
const eventId = Number.isInteger(selector)
|
|
766
786
|
? selector
|
|
767
787
|
: this.eventInfo.findIndex((event) => event.name === selector);
|
|
@@ -794,7 +814,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
794
814
|
this.bufferSampleRatesPtr,
|
|
795
815
|
this.executionOutputPtr,
|
|
796
816
|
);
|
|
797
|
-
if (status === PROCESSOR_EXECUTION_INPUT_REJECTED)
|
|
817
|
+
if (status === PROCESSOR_EXECUTION_INPUT_REJECTED) {
|
|
818
|
+
throw new ProcessorExecutionError(
|
|
819
|
+
`event '${event.name}' input rejected: invalid payload or insufficient workspace`,
|
|
820
|
+
status,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
798
823
|
this.publishExecutionOutput(
|
|
799
824
|
EXECUTION_OPERATION_EVENT,
|
|
800
825
|
eventId,
|
|
@@ -1559,8 +1584,8 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1559
1584
|
this.blockSize - startFrame,
|
|
1560
1585
|
);
|
|
1561
1586
|
const endsBlock = startFrame + segmentFrames === this.blockSize;
|
|
1562
|
-
const flags = (startFrame === 0 ?
|
|
1563
|
-
| (endsBlock ?
|
|
1587
|
+
const flags = (startFrame === 0 ? PROCESSOR_BEGIN_BLOCK : 0)
|
|
1588
|
+
| (endsBlock ? PROCESSOR_END_BLOCK : 0);
|
|
1564
1589
|
|
|
1565
1590
|
this.marshalInputSegment(
|
|
1566
1591
|
inputs,
|
|
@@ -1578,12 +1603,13 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1578
1603
|
EXECUTION_OPERATION_PROCESS,
|
|
1579
1604
|
0,
|
|
1580
1605
|
);
|
|
1581
|
-
if (status !==
|
|
1606
|
+
if (status !== PROCESSOR_EXECUTION_OK) {
|
|
1582
1607
|
this.invalidateState();
|
|
1583
1608
|
this.clearOutputs(outputs);
|
|
1584
1609
|
this.port.postMessage({
|
|
1585
1610
|
type: "onda-error",
|
|
1586
1611
|
operation: "process",
|
|
1612
|
+
status,
|
|
1587
1613
|
error: `processor process failed with Onda execution status ${String(status)}`,
|
|
1588
1614
|
});
|
|
1589
1615
|
return true;
|