@onda-lang/webaudio 0.6.0 → 0.7.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 +23 -3
- package/package.json +2 -2
- package/src/index.d.ts +4 -0
- package/src/worklet.js +56 -15
package/README.md
CHANGED
|
@@ -90,9 +90,29 @@ After construction, the normal f32 render callback reuses cached Wasm-memory vie
|
|
|
90
90
|
host-side allocation or memory growth. Full-block f32 inputs and outputs use typed-array bulk copies;
|
|
91
91
|
segmented callbacks and other ABI scalar widths use preallocated typed views with conversion loops
|
|
92
92
|
(i64 input conversion necessarily creates JavaScript `BigInt` values). External buffers are copied
|
|
93
|
-
into Wasm with typed-array bulk operations during construction.
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
into Wasm with typed-array bulk operations during construction. Missing or `null` bindings install
|
|
94
|
+
neutral one-frame descriptors: reads return zero, writes are discarded, exact channel counts are
|
|
95
|
+
retained, and dynamic-channel buffers report one channel. Supplied bindings must still contain
|
|
96
|
+
nonempty, correctly shaped data.
|
|
97
|
+
|
|
98
|
+
Fixed buffer arrays may be supplied under their logical group name. Each slot is independent:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
const processor = await createOndaAudioProcessor(context, artifact, {
|
|
102
|
+
buffers: {
|
|
103
|
+
impulse: { data: impulseSamples, channels: 1, sampleRate: 48_000 },
|
|
104
|
+
bank: [
|
|
105
|
+
null,
|
|
106
|
+
{ data: secondSamples, channels: 1, sampleRate: 48_000 },
|
|
107
|
+
// Remaining slots are neutral.
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Hosts may instead pass a flat array in physical descriptor order or key individual physical names
|
|
114
|
+
such as `"bank[1]"`. Logical group metadata determines the contiguous slot range; no sample data is
|
|
115
|
+
copied when Onda selects a slot while processing.
|
|
96
116
|
|
|
97
117
|
Artifact descriptors and module exports are validated by the shared, compiler-free
|
|
98
118
|
`@onda-lang/processor-abi` package before anything reaches the rendering thread.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onda-lang/webaudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Optional Web Audio adapter for Onda processor WebAssembly artifacts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"test": "node --test"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@onda-lang/processor-abi": "0.
|
|
35
|
+
"@onda-lang/processor-abi": "0.7.0"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
package/src/index.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface OndaAudioProcessorOptions {
|
|
|
22
22
|
workletUrl?: string | URL;
|
|
23
23
|
/** Initial plain Onda parameter values, keyed or ordered by descriptor parameter. */
|
|
24
24
|
params?: Record<string, unknown> | unknown[];
|
|
25
|
+
/**
|
|
26
|
+
* Initial external-buffer bindings keyed by physical name, grouped by logical array name, or
|
|
27
|
+
* ordered by physical descriptor slot. Missing and null slots use neutral one-frame storage.
|
|
28
|
+
*/
|
|
25
29
|
buffers?: Record<string, unknown> | unknown[];
|
|
26
30
|
/** Preallocated capacity for dynamic event payloads. Defaults to 64 KiB. */
|
|
27
31
|
eventPayloadCapacityBytes?: number;
|
package/src/worklet.js
CHANGED
|
@@ -70,6 +70,9 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
70
70
|
this.bufferInfo = Array.isArray(metadata.metadata?.buffers)
|
|
71
71
|
? metadata.metadata.buffers
|
|
72
72
|
: [];
|
|
73
|
+
this.bufferArrayInfo = Array.isArray(metadata.metadata?.buffer_arrays)
|
|
74
|
+
? metadata.metadata.buffer_arrays
|
|
75
|
+
: [];
|
|
73
76
|
this.inputInfo = Array.isArray(metadata.metadata?.inputs)
|
|
74
77
|
? metadata.metadata.inputs
|
|
75
78
|
: [];
|
|
@@ -607,11 +610,21 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
607
610
|
|
|
608
611
|
bindInitialBuffers(options) {
|
|
609
612
|
this.bufferInfo.forEach((buffer, bufferId) => {
|
|
610
|
-
const supplied =
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (supplied === undefined) {
|
|
614
|
-
|
|
613
|
+
const supplied = this.initialBufferOption(options, buffer, bufferId);
|
|
614
|
+
const declaredChannels = Number(buffer.static_channels ?? 0);
|
|
615
|
+
const fallbackChannels = declaredChannels || 1;
|
|
616
|
+
if (supplied === undefined || supplied === null) {
|
|
617
|
+
const sampleRate = Math.fround(this.compileSampleRate);
|
|
618
|
+
this.writeBufferDescriptor(bufferId, 0, 1, fallbackChannels, sampleRate);
|
|
619
|
+
this.bufferBindings[bufferId] = {
|
|
620
|
+
...buffer,
|
|
621
|
+
pointer: 0,
|
|
622
|
+
frames: 1,
|
|
623
|
+
channels: fallbackChannels,
|
|
624
|
+
sampleRate,
|
|
625
|
+
bound: false,
|
|
626
|
+
};
|
|
627
|
+
return;
|
|
615
628
|
}
|
|
616
629
|
const descriptor =
|
|
617
630
|
Array.isArray(supplied) || ArrayBuffer.isView(supplied)
|
|
@@ -632,7 +645,6 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
632
645
|
`Onda buffer '${buffer.name}' requires non-empty bound data`,
|
|
633
646
|
);
|
|
634
647
|
}
|
|
635
|
-
const declaredChannels = Number(buffer.static_channels ?? 0);
|
|
636
648
|
const channels = Number(descriptor.channels ?? declaredChannels);
|
|
637
649
|
if (!Number.isInteger(channels) || channels <= 0) {
|
|
638
650
|
throw new Error(`Onda buffer '${buffer.name}' requires channels > 0`);
|
|
@@ -655,25 +667,54 @@ class OndaWasmProcessor extends AudioWorkletProcessor {
|
|
|
655
667
|
const elementSize = this.scalarByteSize(buffer.scalar);
|
|
656
668
|
const pointer = this.alloc(length * elementSize, elementSize);
|
|
657
669
|
this.writeScalarValues(pointer, buffer.scalar, data, length);
|
|
658
|
-
|
|
659
|
-
view.setUint32(this.bufferPointersPtr + bufferId * 4, pointer, true);
|
|
660
|
-
view.setInt32(this.bufferFramesPtr + bufferId * 4, frames, true);
|
|
661
|
-
view.setInt32(this.bufferChannelsPtr + bufferId * 4, channels, true);
|
|
662
|
-
view.setFloat32(
|
|
663
|
-
this.bufferSampleRatesPtr + bufferId * 4,
|
|
664
|
-
sampleRate,
|
|
665
|
-
true,
|
|
666
|
-
);
|
|
670
|
+
this.writeBufferDescriptor(bufferId, pointer, frames, channels, sampleRate);
|
|
667
671
|
this.bufferBindings[bufferId] = {
|
|
668
672
|
...buffer,
|
|
669
673
|
pointer,
|
|
670
674
|
frames,
|
|
671
675
|
channels,
|
|
672
676
|
sampleRate,
|
|
677
|
+
bound: true,
|
|
673
678
|
};
|
|
674
679
|
});
|
|
675
680
|
}
|
|
676
681
|
|
|
682
|
+
writeBufferDescriptor(bufferId, pointer, frames, channels, sampleRate) {
|
|
683
|
+
const view = this.memoryView();
|
|
684
|
+
view.setUint32(this.bufferPointersPtr + bufferId * 4, pointer, true);
|
|
685
|
+
view.setInt32(this.bufferFramesPtr + bufferId * 4, frames, true);
|
|
686
|
+
view.setInt32(this.bufferChannelsPtr + bufferId * 4, channels, true);
|
|
687
|
+
view.setFloat32(this.bufferSampleRatesPtr + bufferId * 4, sampleRate, true);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
initialBufferOption(options, buffer, bufferId) {
|
|
691
|
+
if (Array.isArray(options)) {
|
|
692
|
+
return options[bufferId];
|
|
693
|
+
}
|
|
694
|
+
if (!options || typeof options !== "object") {
|
|
695
|
+
throw new Error("processorOptions.buffers must be an array or object");
|
|
696
|
+
}
|
|
697
|
+
if (Object.prototype.hasOwnProperty.call(options, buffer.name)) {
|
|
698
|
+
return options[buffer.name];
|
|
699
|
+
}
|
|
700
|
+
const group = this.bufferArrayInfo.find((candidate) => {
|
|
701
|
+
const first = Number(candidate.first_buffer);
|
|
702
|
+
const len = Number(candidate.len);
|
|
703
|
+
return bufferId >= first && bufferId < first + len;
|
|
704
|
+
});
|
|
705
|
+
if (!group || !Object.prototype.hasOwnProperty.call(options, group.name)) {
|
|
706
|
+
return undefined;
|
|
707
|
+
}
|
|
708
|
+
const supplied = options[group.name];
|
|
709
|
+
if (supplied === undefined || supplied === null) {
|
|
710
|
+
return undefined;
|
|
711
|
+
}
|
|
712
|
+
if (!Array.isArray(supplied)) {
|
|
713
|
+
throw new Error(`Onda buffer array '${group.name}' must be an array`);
|
|
714
|
+
}
|
|
715
|
+
return supplied[bufferId - Number(group.first_buffer)];
|
|
716
|
+
}
|
|
717
|
+
|
|
677
718
|
readBuffer(selector) {
|
|
678
719
|
const bufferId = Number.isInteger(selector)
|
|
679
720
|
? selector
|