@onda-lang/webaudio 0.7.5 → 0.8.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.
- package/README.md +124 -14
- package/api.md +407 -0
- package/package.json +5 -3
- package/src/execution-output-ring.js +234 -0
- package/src/index.d.ts +75 -3
- package/src/index.js +472 -10
- package/src/worklet.js +414 -45
package/src/worklet.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXECUTION_OPERATION_EVENT,
|
|
3
|
+
EXECUTION_OPERATION_INIT,
|
|
4
|
+
EXECUTION_OPERATION_PROCESS,
|
|
5
|
+
EXECUTION_OPERATION_TRANSPORT,
|
|
6
|
+
openExecutionOutputRing,
|
|
7
|
+
writeExecutionOutputRing,
|
|
8
|
+
} from "./execution-output-ring.js";
|
|
9
|
+
|
|
1
10
|
const ONDA_PROCESS_BEGIN_BLOCK = 1 << 0;
|
|
2
11
|
const ONDA_PROCESS_END_BLOCK = 1 << 1;
|
|
12
|
+
const ONDA_INIT_PRESERVE_PINNED = 0;
|
|
13
|
+
const ONDA_INIT_FULL = 1;
|
|
3
14
|
const ONDA_AUDIO_WORKLET_PROCESSOR_NAME = "onda-wasm-processor";
|
|
4
15
|
const DEFAULT_EVENT_PAYLOAD_CAPACITY_BYTES = 64 * 1024;
|
|
16
|
+
const DEFAULT_DELEGATE_CAPACITY_BYTES = 64 * 1024;
|
|
17
|
+
const DEFAULT_PRINT_CAPACITY_BYTES = 64 * 1024;
|
|
18
|
+
const DELEGATE_BATCH_SIZE_BYTES = 20;
|
|
19
|
+
const PRINT_BATCH_SIZE_BYTES = 20;
|
|
20
|
+
const EXECUTION_OUTPUT_SIZE_BYTES = 12;
|
|
5
21
|
const HOST_LITTLE_ENDIAN = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
|
|
6
22
|
|
|
7
23
|
class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
@@ -47,6 +63,8 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
47
63
|
this.stateBytes = null;
|
|
48
64
|
this.inputViews = [];
|
|
49
65
|
this.outputViews = [];
|
|
66
|
+
this.delegateRecordView = null;
|
|
67
|
+
this.printRecordView = null;
|
|
50
68
|
|
|
51
69
|
const heapBase = Number(
|
|
52
70
|
this.exports.__heap_base?.value ?? this.exports.__heap_base ?? 0,
|
|
@@ -64,6 +82,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
64
82
|
this.eventInfo = Array.isArray(metadata.metadata?.events)
|
|
65
83
|
? metadata.metadata.events
|
|
66
84
|
: [];
|
|
85
|
+
this.delegateInfo = Array.isArray(metadata.metadata?.delegates)
|
|
86
|
+
? metadata.metadata.delegates
|
|
87
|
+
: [];
|
|
88
|
+
this.logSiteInfo = Array.isArray(metadata.metadata?.log_sites)
|
|
89
|
+
? metadata.metadata.log_sites
|
|
90
|
+
: [];
|
|
67
91
|
this.controlOutputInfo = Array.isArray(metadata.metadata?.control_outputs)
|
|
68
92
|
? metadata.metadata.control_outputs
|
|
69
93
|
: [];
|
|
@@ -115,7 +139,6 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
115
139
|
this.outputPtrs = [];
|
|
116
140
|
this.outputCapacityFrames = 0;
|
|
117
141
|
this.blockCursor = 0;
|
|
118
|
-
this.executionFailed = false;
|
|
119
142
|
|
|
120
143
|
const paramBytes = Number(metadata.runtime?.param_size_bytes ?? 0);
|
|
121
144
|
if (!Number.isInteger(paramBytes) || paramBytes < 0) {
|
|
@@ -148,13 +171,101 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
148
171
|
this.eventPayloadPtr = this.eventPayloadCapacity
|
|
149
172
|
? this.alloc(this.eventPayloadCapacity, 8)
|
|
150
173
|
: 0;
|
|
174
|
+
this.delegateCapacity = this.configuredDelegateCapacity(
|
|
175
|
+
processorOptions.delegateCapacityBytes,
|
|
176
|
+
);
|
|
177
|
+
this.delegateStoragePtr = this.delegateInfo.length && this.delegateCapacity
|
|
178
|
+
? this.alloc(this.delegateCapacity, 8)
|
|
179
|
+
: 0;
|
|
180
|
+
this.delegateBatchPtr = this.delegateInfo.length
|
|
181
|
+
? this.alloc(DELEGATE_BATCH_SIZE_BYTES, 4)
|
|
182
|
+
: 0;
|
|
183
|
+
if (this.delegateBatchPtr) {
|
|
184
|
+
const view = new DataView(this.memory.buffer);
|
|
185
|
+
view.setUint32(this.delegateBatchPtr, this.delegateStoragePtr, true);
|
|
186
|
+
view.setUint32(this.delegateBatchPtr + 4, this.delegateCapacity, true);
|
|
187
|
+
view.setUint32(this.delegateBatchPtr + 8, 0, true);
|
|
188
|
+
view.setUint32(this.delegateBatchPtr + 12, 0, true);
|
|
189
|
+
view.setUint32(this.delegateBatchPtr + 16, 0, true);
|
|
190
|
+
}
|
|
191
|
+
this.printCapacity = this.configuredPrintCapacity(
|
|
192
|
+
processorOptions.printCapacityBytes,
|
|
193
|
+
);
|
|
194
|
+
this.printStoragePtr = this.logSiteInfo.length && this.printCapacity
|
|
195
|
+
? this.alloc(this.printCapacity, 8)
|
|
196
|
+
: 0;
|
|
197
|
+
this.printBatchPtr = this.logSiteInfo.length
|
|
198
|
+
? this.alloc(PRINT_BATCH_SIZE_BYTES, 4)
|
|
199
|
+
: 0;
|
|
200
|
+
if (this.printBatchPtr) {
|
|
201
|
+
const view = new DataView(this.memory.buffer);
|
|
202
|
+
view.setUint32(this.printBatchPtr, this.printStoragePtr, true);
|
|
203
|
+
view.setUint32(this.printBatchPtr + 4, this.printCapacity, true);
|
|
204
|
+
view.setUint32(this.printBatchPtr + 8, 0, true);
|
|
205
|
+
view.setUint32(this.printBatchPtr + 12, 0, true);
|
|
206
|
+
view.setUint32(this.printBatchPtr + 16, 0, true);
|
|
207
|
+
}
|
|
208
|
+
this.printCollectionEnabled = processorOptions.printCollectionEnabled === true
|
|
209
|
+
&& this.printStoragePtr !== 0;
|
|
210
|
+
this.printSubscriptionId = Number.isSafeInteger(processorOptions.printSubscriptionId)
|
|
211
|
+
? processorOptions.printSubscriptionId
|
|
212
|
+
: 0;
|
|
213
|
+
this.executionOutputPtr = this.delegateBatchPtr || this.printBatchPtr
|
|
214
|
+
? this.alloc(EXECUTION_OUTPUT_SIZE_BYTES, 4)
|
|
215
|
+
: 0;
|
|
216
|
+
if (this.executionOutputPtr) {
|
|
217
|
+
const view = new DataView(this.memory.buffer);
|
|
218
|
+
view.setUint32(this.executionOutputPtr, 0, true);
|
|
219
|
+
view.setUint32(
|
|
220
|
+
this.executionOutputPtr + 4,
|
|
221
|
+
this.printCollectionEnabled ? this.printBatchPtr : 0,
|
|
222
|
+
true,
|
|
223
|
+
);
|
|
224
|
+
view.setUint32(this.executionOutputPtr + 8, 0, true);
|
|
225
|
+
}
|
|
226
|
+
this.delegateCollectionEnabled = false;
|
|
227
|
+
this.delegateSubscriptionId = 0;
|
|
228
|
+
this.executionOutputRing = processorOptions.executionOutputRing === undefined
|
|
229
|
+
|| processorOptions.executionOutputRing === null
|
|
230
|
+
? null
|
|
231
|
+
: openExecutionOutputRing(processorOptions.executionOutputRing);
|
|
232
|
+
if (
|
|
233
|
+
this.executionOutputRing
|
|
234
|
+
&& (
|
|
235
|
+
this.executionOutputRing.delegateCapacity !== this.delegateCapacity
|
|
236
|
+
|| this.executionOutputRing.printCapacity !== this.printCapacity
|
|
237
|
+
)
|
|
238
|
+
) {
|
|
239
|
+
throw new Error("execution-output ring capacities do not match processor storage");
|
|
240
|
+
}
|
|
241
|
+
this.pendingDelegateDrops = 0;
|
|
242
|
+
this.pendingDelegateOverflow = 0;
|
|
243
|
+
this.pendingPrintDrops = 0;
|
|
244
|
+
this.pendingPrintOverflow = 0;
|
|
245
|
+
this.executionOutputEntry = {
|
|
246
|
+
operation: 0,
|
|
247
|
+
operationIndex: 0,
|
|
248
|
+
delegateSubscriptionId: 0,
|
|
249
|
+
delegateUsed: 0,
|
|
250
|
+
delegateRecordCount: 0,
|
|
251
|
+
delegateOverflowCount: 0,
|
|
252
|
+
delegateTransportDropCount: 0,
|
|
253
|
+
printSubscriptionId: 0,
|
|
254
|
+
printUsed: 0,
|
|
255
|
+
printRecordCount: 0,
|
|
256
|
+
printOverflowCount: 0,
|
|
257
|
+
printTransportDropCount: 0,
|
|
258
|
+
};
|
|
151
259
|
this.writeParamDefaults();
|
|
152
260
|
this.writeInitialParams(processorOptions.params ?? {});
|
|
153
261
|
this.ensureInputCapacity(this.blockSize);
|
|
154
262
|
this.ensureOutputCapacity(this.blockSize);
|
|
155
263
|
this.viewsReady = true;
|
|
156
264
|
this.refreshMemoryCache(true);
|
|
157
|
-
this.
|
|
265
|
+
this.invalidateState();
|
|
266
|
+
if (processorOptions.initialize === true) {
|
|
267
|
+
this.init(ONDA_INIT_FULL);
|
|
268
|
+
}
|
|
158
269
|
this.allocationLocked = true;
|
|
159
270
|
this.port.onmessage = (event) => this.handleMessage(event.data ?? {});
|
|
160
271
|
}
|
|
@@ -175,7 +286,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
175
286
|
|
|
176
287
|
alloc(size, align) {
|
|
177
288
|
if (this.allocationLocked) {
|
|
178
|
-
throw new Error("Onda worklet memory allocation is locked after
|
|
289
|
+
throw new Error("Onda worklet memory allocation is locked after construction");
|
|
179
290
|
}
|
|
180
291
|
const ptr = this.alignUp(this.heap, align);
|
|
181
292
|
const next = ptr + size;
|
|
@@ -212,6 +323,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
212
323
|
this.outputCapacityFrames,
|
|
213
324
|
);
|
|
214
325
|
});
|
|
326
|
+
this.delegateRecordView = this.delegateStoragePtr
|
|
327
|
+
? new Uint8Array(buffer, this.delegateStoragePtr, this.delegateCapacity)
|
|
328
|
+
: null;
|
|
329
|
+
this.printRecordView = this.printStoragePtr
|
|
330
|
+
? new Uint8Array(buffer, this.printStoragePtr, this.printCapacity)
|
|
331
|
+
: null;
|
|
215
332
|
}
|
|
216
333
|
|
|
217
334
|
memoryView() {
|
|
@@ -255,6 +372,32 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
255
372
|
return capacity;
|
|
256
373
|
}
|
|
257
374
|
|
|
375
|
+
configuredDelegateCapacity(configuredCapacity) {
|
|
376
|
+
const capacity = configuredCapacity
|
|
377
|
+
?? (this.delegateInfo.length ? DEFAULT_DELEGATE_CAPACITY_BYTES : 0);
|
|
378
|
+
if (
|
|
379
|
+
!Number.isSafeInteger(capacity)
|
|
380
|
+
|| capacity < 0
|
|
381
|
+
|| capacity > 0x7fff_ffff
|
|
382
|
+
) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
"delegate capacity must be an integer from 0 through 2147483647 bytes",
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
return capacity;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
configuredPrintCapacity(configuredCapacity) {
|
|
391
|
+
const capacity = configuredCapacity
|
|
392
|
+
?? (this.logSiteInfo.length ? DEFAULT_PRINT_CAPACITY_BYTES : 0);
|
|
393
|
+
if (!Number.isSafeInteger(capacity) || capacity < 0 || capacity > 0x7fff_ffff) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
"print capacity must be an integer from 0 through 2147483647 bytes",
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
return capacity;
|
|
399
|
+
}
|
|
400
|
+
|
|
258
401
|
flattenAudioChannels(ports, kind) {
|
|
259
402
|
const channels = [];
|
|
260
403
|
for (const port of ports) {
|
|
@@ -349,24 +492,73 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
349
492
|
);
|
|
350
493
|
}
|
|
351
494
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
495
|
+
init(mode) {
|
|
496
|
+
if (mode !== ONDA_INIT_PRESERVE_PINNED && mode !== ONDA_INIT_FULL) {
|
|
497
|
+
throw new Error(`invalid Onda init mode '${String(mode)}'`);
|
|
498
|
+
}
|
|
499
|
+
if (mode === ONDA_INIT_PRESERVE_PINNED && !this.initialized) {
|
|
500
|
+
throw new Error("full initialization is required before preserving pinned state");
|
|
501
|
+
}
|
|
502
|
+
this.runInitialization(mode);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
invalidateState() {
|
|
506
|
+
this.initialized = false;
|
|
507
|
+
this.process = this.processPending;
|
|
355
508
|
this.blockCursor = 0;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
commitInitializedState(blockCursor) {
|
|
512
|
+
this.initialized = true;
|
|
513
|
+
this.process = this.processInitialized;
|
|
514
|
+
this.blockCursor = blockCursor;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
runInitialization(mode, afterInitialize) {
|
|
518
|
+
// Reinitializing state does not create a compile-block boundary. Retain
|
|
519
|
+
// the host-side position so the next process segment cannot synthesize an
|
|
520
|
+
// extra BEGIN_BLOCK or postpone the matching END_BLOCK.
|
|
521
|
+
const blockCursor = this.initialized ? this.blockCursor : 0;
|
|
522
|
+
// Generated initialization mutates the live image in place. Stop exposing
|
|
523
|
+
// it before entering Wasm so a failure, including one in snapshot overlay,
|
|
524
|
+
// leaves the processor on the silent pending path.
|
|
525
|
+
this.invalidateState();
|
|
526
|
+
this.refreshMemoryCache();
|
|
527
|
+
this.prepareExecutionOutput();
|
|
528
|
+
const status = this.exports.onda_processor_init(
|
|
529
|
+
this.paramsPtr,
|
|
530
|
+
this.statePtr,
|
|
531
|
+
mode,
|
|
532
|
+
this.bufferPointersPtr,
|
|
533
|
+
this.bufferFramesPtr,
|
|
534
|
+
this.bufferChannelsPtr,
|
|
535
|
+
this.bufferSampleRatesPtr,
|
|
536
|
+
this.executionOutputPtr,
|
|
360
537
|
);
|
|
538
|
+
// Generated failures retain diagnostic prints but clear delegates.
|
|
539
|
+
this.publishExecutionOutput(
|
|
540
|
+
EXECUTION_OPERATION_INIT,
|
|
541
|
+
0,
|
|
542
|
+
);
|
|
543
|
+
this.checkExecutionStatus(status, "processor init");
|
|
544
|
+
afterInitialize?.();
|
|
545
|
+
this.commitInitializedState(blockCursor);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
requireInitialized(operation) {
|
|
549
|
+
if (!this.initialized) {
|
|
550
|
+
throw new Error(`full initialization is required before ${operation}`);
|
|
551
|
+
}
|
|
361
552
|
}
|
|
362
553
|
|
|
363
554
|
checkExecutionStatus(status, operation) {
|
|
364
555
|
if (status === 0) return;
|
|
365
|
-
this.
|
|
556
|
+
this.invalidateState();
|
|
366
557
|
throw new Error(`${operation} failed with Onda execution status ${String(status)}`);
|
|
367
558
|
}
|
|
368
559
|
|
|
369
560
|
createSnapshot() {
|
|
561
|
+
this.requireInitialized("snapshot");
|
|
370
562
|
const snapshot = new Uint8Array(this.snapshotSizeBytes);
|
|
371
563
|
this.refreshMemoryCache();
|
|
372
564
|
const state = this.stateBytes;
|
|
@@ -391,20 +583,22 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
391
583
|
);
|
|
392
584
|
}
|
|
393
585
|
// The ABI restore base is a fresh post-init image, so scratch and
|
|
394
|
-
// control-mirror state never leak across a restore.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
586
|
+
// control-mirror state never leak across a restore. Initialization and
|
|
587
|
+
// overlay form one lifecycle transition: neither partial result is ready.
|
|
588
|
+
this.runInitialization(ONDA_INIT_FULL, () => {
|
|
589
|
+
const state = this.stateBytes;
|
|
590
|
+
for (const entry of this.snapshotInfo) {
|
|
591
|
+
const packedOffset = Number(entry.packed_snapshot_byte_offset);
|
|
592
|
+
const physicalOffset = Number(entry.physical_state_byte_offset);
|
|
593
|
+
const byteSize = Number(entry.byte_size);
|
|
594
|
+
this.validateSnapshotEntry(entry, packedOffset, physicalOffset, byteSize);
|
|
595
|
+
state.set(
|
|
596
|
+
snapshot.subarray(packedOffset, packedOffset + byteSize),
|
|
597
|
+
physicalOffset,
|
|
598
|
+
);
|
|
599
|
+
this.normalizeSnapshotIntegerRange(entry, state, physicalOffset, byteSize);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
408
602
|
}
|
|
409
603
|
|
|
410
604
|
normalizeSnapshotIntegerRange(entry, state, offset, byteSize) {
|
|
@@ -494,8 +688,8 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
494
688
|
message.value,
|
|
495
689
|
);
|
|
496
690
|
this.postResponse(message, { type: "onda-ok", operation: message.type });
|
|
497
|
-
} else if (message.type === "
|
|
498
|
-
this.
|
|
691
|
+
} else if (message.type === "init") {
|
|
692
|
+
this.init(message.mode);
|
|
499
693
|
this.postResponse(message, { type: "onda-ok", operation: message.type });
|
|
500
694
|
} else if (message.type === "event") {
|
|
501
695
|
this.dispatchEvent(
|
|
@@ -525,6 +719,10 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
525
719
|
} else if (message.type === "restore-snapshot") {
|
|
526
720
|
this.restoreSnapshot(message.snapshot ?? message.bytes);
|
|
527
721
|
this.postResponse(message, { type: "onda-ok", operation: message.type });
|
|
722
|
+
} else if (message.type === "delegate-subscription") {
|
|
723
|
+
this.setDelegateSubscription(message.enabled, message.subscriptionId);
|
|
724
|
+
} else if (message.type === "print-subscription") {
|
|
725
|
+
this.setPrintSubscription(message.enabled, message.subscriptionId);
|
|
528
726
|
} else {
|
|
529
727
|
throw new Error(`unknown Onda worklet operation '${String(message.type)}'`);
|
|
530
728
|
}
|
|
@@ -539,6 +737,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
539
737
|
}
|
|
540
738
|
|
|
541
739
|
dispatchEvent(selector, values) {
|
|
740
|
+
this.requireInitialized("event dispatch");
|
|
542
741
|
const eventId = Number.isInteger(selector)
|
|
543
742
|
? selector
|
|
544
743
|
: this.eventInfo.findIndex((event) => event.name === selector);
|
|
@@ -598,18 +797,22 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
598
797
|
if (typeof handler !== "function") {
|
|
599
798
|
throw new Error(`missing WebAssembly export '${event.export}'`);
|
|
600
799
|
}
|
|
601
|
-
this.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
`event '${event.name}'`,
|
|
800
|
+
this.prepareExecutionOutput();
|
|
801
|
+
const status = handler(
|
|
802
|
+
this.eventPayloadPtr,
|
|
803
|
+
this.paramsPtr,
|
|
804
|
+
this.statePtr,
|
|
805
|
+
this.bufferPointersPtr,
|
|
806
|
+
this.bufferFramesPtr,
|
|
807
|
+
this.bufferChannelsPtr,
|
|
808
|
+
this.bufferSampleRatesPtr,
|
|
809
|
+
this.executionOutputPtr,
|
|
612
810
|
);
|
|
811
|
+
this.publishExecutionOutput(
|
|
812
|
+
EXECUTION_OPERATION_EVENT,
|
|
813
|
+
eventId,
|
|
814
|
+
);
|
|
815
|
+
this.checkExecutionStatus(status, `event '${event.name}'`);
|
|
613
816
|
}
|
|
614
817
|
|
|
615
818
|
eventValue(event, param, paramId, values) {
|
|
@@ -641,6 +844,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
641
844
|
}
|
|
642
845
|
|
|
643
846
|
readControlOutputs() {
|
|
847
|
+
this.requireInitialized("reading control outputs");
|
|
644
848
|
return Object.fromEntries(
|
|
645
849
|
this.controlOutputInfo.map((output) => [
|
|
646
850
|
output.name,
|
|
@@ -652,6 +856,142 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
652
856
|
);
|
|
653
857
|
}
|
|
654
858
|
|
|
859
|
+
saturatedAdd(lhs, rhs) {
|
|
860
|
+
return Math.min(0xffff_ffff, lhs + rhs);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
publishExecutionOutput(operation, operationIndex) {
|
|
864
|
+
if (!this.executionOutputRing) return;
|
|
865
|
+
if (
|
|
866
|
+
!this.delegateCollectionEnabled
|
|
867
|
+
&& !this.printCollectionEnabled
|
|
868
|
+
&& !this.pendingDelegateDrops
|
|
869
|
+
&& !this.pendingDelegateOverflow
|
|
870
|
+
&& !this.pendingPrintDrops
|
|
871
|
+
&& !this.pendingPrintOverflow
|
|
872
|
+
) return;
|
|
873
|
+
const view = this.memoryView();
|
|
874
|
+
const delegateUsed = this.delegateCollectionEnabled && this.delegateBatchPtr
|
|
875
|
+
? view.getUint32(this.delegateBatchPtr + 8, true)
|
|
876
|
+
: 0;
|
|
877
|
+
const delegateRecordCount = this.delegateCollectionEnabled && this.delegateBatchPtr
|
|
878
|
+
? view.getUint32(this.delegateBatchPtr + 12, true)
|
|
879
|
+
: 0;
|
|
880
|
+
const delegateOverflowCount = this.delegateCollectionEnabled && this.delegateBatchPtr
|
|
881
|
+
? view.getUint32(this.delegateBatchPtr + 16, true)
|
|
882
|
+
: 0;
|
|
883
|
+
const printUsed = this.printCollectionEnabled && this.printBatchPtr
|
|
884
|
+
? view.getUint32(this.printBatchPtr + 8, true)
|
|
885
|
+
: 0;
|
|
886
|
+
const printRecordCount = this.printCollectionEnabled && this.printBatchPtr
|
|
887
|
+
? view.getUint32(this.printBatchPtr + 12, true)
|
|
888
|
+
: 0;
|
|
889
|
+
const printOverflowCount = this.printCollectionEnabled && this.printBatchPtr
|
|
890
|
+
? view.getUint32(this.printBatchPtr + 16, true)
|
|
891
|
+
: 0;
|
|
892
|
+
if (delegateUsed > this.delegateCapacity) {
|
|
893
|
+
throw new Error("processor returned an invalid delegate byte count");
|
|
894
|
+
}
|
|
895
|
+
if (printUsed > this.printCapacity) {
|
|
896
|
+
throw new Error("processor returned an invalid print byte count");
|
|
897
|
+
}
|
|
898
|
+
const hasCurrent = delegateUsed
|
|
899
|
+
|| delegateRecordCount
|
|
900
|
+
|| delegateOverflowCount
|
|
901
|
+
|| printUsed
|
|
902
|
+
|| printRecordCount
|
|
903
|
+
|| printOverflowCount;
|
|
904
|
+
const hasPending = this.pendingDelegateDrops
|
|
905
|
+
|| this.pendingDelegateOverflow
|
|
906
|
+
|| this.pendingPrintDrops
|
|
907
|
+
|| this.pendingPrintOverflow;
|
|
908
|
+
if (!hasCurrent && !hasPending) return;
|
|
909
|
+
|
|
910
|
+
const entry = this.executionOutputEntry;
|
|
911
|
+
entry.operation = hasCurrent ? operation : EXECUTION_OPERATION_TRANSPORT;
|
|
912
|
+
entry.operationIndex = hasCurrent ? operationIndex : 0;
|
|
913
|
+
entry.delegateSubscriptionId = this.delegateSubscriptionId;
|
|
914
|
+
entry.delegateUsed = delegateUsed;
|
|
915
|
+
entry.delegateRecordCount = delegateRecordCount;
|
|
916
|
+
entry.delegateOverflowCount = this.saturatedAdd(
|
|
917
|
+
this.pendingDelegateOverflow,
|
|
918
|
+
delegateOverflowCount,
|
|
919
|
+
);
|
|
920
|
+
entry.delegateTransportDropCount = this.pendingDelegateDrops;
|
|
921
|
+
entry.printSubscriptionId = this.printSubscriptionId;
|
|
922
|
+
entry.printUsed = printUsed;
|
|
923
|
+
entry.printRecordCount = printRecordCount;
|
|
924
|
+
entry.printOverflowCount = this.saturatedAdd(
|
|
925
|
+
this.pendingPrintOverflow,
|
|
926
|
+
printOverflowCount,
|
|
927
|
+
);
|
|
928
|
+
entry.printTransportDropCount = this.pendingPrintDrops;
|
|
929
|
+
if (writeExecutionOutputRing(
|
|
930
|
+
this.executionOutputRing,
|
|
931
|
+
entry,
|
|
932
|
+
this.delegateRecordView,
|
|
933
|
+
this.printRecordView,
|
|
934
|
+
)) {
|
|
935
|
+
this.pendingDelegateDrops = 0;
|
|
936
|
+
this.pendingDelegateOverflow = 0;
|
|
937
|
+
this.pendingPrintDrops = 0;
|
|
938
|
+
this.pendingPrintOverflow = 0;
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
this.pendingDelegateDrops = this.saturatedAdd(
|
|
942
|
+
this.pendingDelegateDrops,
|
|
943
|
+
delegateRecordCount,
|
|
944
|
+
);
|
|
945
|
+
this.pendingDelegateOverflow = this.saturatedAdd(
|
|
946
|
+
this.pendingDelegateOverflow,
|
|
947
|
+
delegateOverflowCount,
|
|
948
|
+
);
|
|
949
|
+
this.pendingPrintDrops = this.saturatedAdd(
|
|
950
|
+
this.pendingPrintDrops,
|
|
951
|
+
printRecordCount,
|
|
952
|
+
);
|
|
953
|
+
this.pendingPrintOverflow = this.saturatedAdd(
|
|
954
|
+
this.pendingPrintOverflow,
|
|
955
|
+
printOverflowCount,
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
setDelegateSubscription(enabled, subscriptionId) {
|
|
960
|
+
this.delegateCollectionEnabled = enabled === true && this.delegateStoragePtr !== 0;
|
|
961
|
+
this.delegateSubscriptionId = Number.isSafeInteger(subscriptionId)
|
|
962
|
+
? subscriptionId
|
|
963
|
+
: 0;
|
|
964
|
+
this.pendingDelegateDrops = 0;
|
|
965
|
+
this.pendingDelegateOverflow = 0;
|
|
966
|
+
if (this.delegateBatchPtr) {
|
|
967
|
+
const view = this.memoryView();
|
|
968
|
+
this.resetRecordBatch(view, this.delegateBatchPtr);
|
|
969
|
+
view.setUint32(
|
|
970
|
+
this.executionOutputPtr,
|
|
971
|
+
this.delegateCollectionEnabled ? this.delegateBatchPtr : 0,
|
|
972
|
+
true,
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
setPrintSubscription(enabled, subscriptionId) {
|
|
978
|
+
this.printCollectionEnabled = enabled === true && this.printStoragePtr !== 0;
|
|
979
|
+
this.printSubscriptionId = Number.isSafeInteger(subscriptionId)
|
|
980
|
+
? subscriptionId
|
|
981
|
+
: 0;
|
|
982
|
+
this.pendingPrintDrops = 0;
|
|
983
|
+
this.pendingPrintOverflow = 0;
|
|
984
|
+
if (this.printBatchPtr) {
|
|
985
|
+
const view = this.memoryView();
|
|
986
|
+
this.resetRecordBatch(view, this.printBatchPtr);
|
|
987
|
+
view.setUint32(
|
|
988
|
+
this.executionOutputPtr + 4,
|
|
989
|
+
this.printCollectionEnabled ? this.printBatchPtr : 0,
|
|
990
|
+
true,
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
655
995
|
bindInitialBuffers(options) {
|
|
656
996
|
this.bufferInfo.forEach((buffer, bufferId) => {
|
|
657
997
|
const supplied = this.initialBufferOption(options, buffer, bufferId);
|
|
@@ -804,7 +1144,8 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
804
1144
|
}
|
|
805
1145
|
|
|
806
1146
|
isFixedArray(info) {
|
|
807
|
-
return info?.
|
|
1147
|
+
return info?.is_array
|
|
1148
|
+
?? (info?.is_slice !== true && /\[[0-9]+\]$/.test(info?.type_repr ?? ""));
|
|
808
1149
|
}
|
|
809
1150
|
|
|
810
1151
|
decodeConstantScalar(scalar) {
|
|
@@ -1198,6 +1539,7 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1198
1539
|
}
|
|
1199
1540
|
|
|
1200
1541
|
invokeProcessSegment(startFrame, frames, flags) {
|
|
1542
|
+
this.prepareExecutionOutput();
|
|
1201
1543
|
return this.exports.onda_process(
|
|
1202
1544
|
this.statePtr,
|
|
1203
1545
|
this.paramsPtr,
|
|
@@ -1210,7 +1552,29 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1210
1552
|
this.bufferFramesPtr,
|
|
1211
1553
|
this.bufferChannelsPtr,
|
|
1212
1554
|
this.bufferSampleRatesPtr,
|
|
1555
|
+
this.executionOutputPtr,
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
resetRecordBatch(view, batchPtr) {
|
|
1560
|
+
if (!batchPtr) return;
|
|
1561
|
+
view.setUint32(batchPtr + 8, 0, true);
|
|
1562
|
+
view.setUint32(batchPtr + 12, 0, true);
|
|
1563
|
+
view.setUint32(batchPtr + 16, 0, true);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
prepareExecutionOutput() {
|
|
1567
|
+
if (!this.executionOutputPtr) return;
|
|
1568
|
+
const view = this.memoryView();
|
|
1569
|
+
this.resetRecordBatch(
|
|
1570
|
+
view,
|
|
1571
|
+
view.getUint32(this.executionOutputPtr, true),
|
|
1572
|
+
);
|
|
1573
|
+
this.resetRecordBatch(
|
|
1574
|
+
view,
|
|
1575
|
+
view.getUint32(this.executionOutputPtr + 4, true),
|
|
1213
1576
|
);
|
|
1577
|
+
view.setUint32(this.executionOutputPtr + 8, 0, true);
|
|
1214
1578
|
}
|
|
1215
1579
|
|
|
1216
1580
|
clearOutputs(outputs) {
|
|
@@ -1219,12 +1583,13 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1219
1583
|
}
|
|
1220
1584
|
}
|
|
1221
1585
|
|
|
1222
|
-
|
|
1586
|
+
processPending(_inputs, outputs) {
|
|
1587
|
+
this.clearOutputs(outputs);
|
|
1588
|
+
return true;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
processInitialized(inputs, outputs) {
|
|
1223
1592
|
this.refreshMemoryCache();
|
|
1224
|
-
if (this.executionFailed) {
|
|
1225
|
-
this.clearOutputs(outputs);
|
|
1226
|
-
return true;
|
|
1227
|
-
}
|
|
1228
1593
|
const frames = this.audioFrameCount(inputs, outputs);
|
|
1229
1594
|
|
|
1230
1595
|
let callbackOffset = 0;
|
|
@@ -1250,8 +1615,12 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
1250
1615
|
segmentFrames,
|
|
1251
1616
|
flags,
|
|
1252
1617
|
);
|
|
1618
|
+
this.publishExecutionOutput(
|
|
1619
|
+
EXECUTION_OPERATION_PROCESS,
|
|
1620
|
+
0,
|
|
1621
|
+
);
|
|
1253
1622
|
if (status !== 0) {
|
|
1254
|
-
this.
|
|
1623
|
+
this.invalidateState();
|
|
1255
1624
|
this.clearOutputs(outputs);
|
|
1256
1625
|
this.port.postMessage({
|
|
1257
1626
|
type: "onda-error",
|