Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AUDIO_WORKLET] Added support for MEMORY64 and 2GB heap (including tests) #23508

Open
wants to merge 73 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
cc43cd8
Logging and notes for me
cwoffenden Oct 16, 2024
5fe9631
Better error message (to see why it fails)
cwoffenden Oct 16, 2024
8a711fe
Create one-time fixed views into the heap
cwoffenden Oct 16, 2024
575027a
Allow the number of channels to increase (or the audio chain to change)
cwoffenden Oct 17, 2024
fe509b6
Work in progress, moved the output buffers first
cwoffenden Oct 18, 2024
ea4f9a9
Interim commit, work-in-progress
cwoffenden Oct 22, 2024
c39928a
Work-in-progress: using a single stack allocation
cwoffenden Oct 25, 2024
8f6a793
WIP: notes and findings
cwoffenden Oct 25, 2024
0bfa410
Correct stack offsets and verified code
cwoffenden Oct 28, 2024
0935e01
Added more assertions, minor docs
cwoffenden Oct 29, 2024
8dfe26d
Explicitly assert any changes to the stack address
cwoffenden Oct 31, 2024
9afee91
Added sample files
cwoffenden Nov 1, 2024
663ef89
Work-in-progress
cwoffenden Nov 1, 2024
4024368
Initial mixer
cwoffenden Nov 8, 2024
38d3425
Missing blank line
cwoffenden Nov 8, 2024
fc3476a
Work-in-progress (reusable audio creation and playback)
cwoffenden Nov 14, 2024
f6a78ae
Tidied mixer
cwoffenden Nov 15, 2024
ac98c9e
Typo
cwoffenden Nov 15, 2024
5163588
Added test harness hooks
cwoffenden Nov 15, 2024
dbc7cee
Added description of the test
cwoffenden Nov 15, 2024
4e9d358
Added the web audio mixer to the browser tests
cwoffenden Nov 15, 2024
55e70fa
STRICT will fail without a filled INCOMING_MODULE_JS_API
cwoffenden Nov 15, 2024
392fede
Added two audio ins to two audio outs test
cwoffenden Nov 15, 2024
56676a4
Added the mono tests
cwoffenden Nov 15, 2024
2ff5c87
Formatting
cwoffenden Nov 15, 2024
20b222b
Fixes to build with MEMORY64
cwoffenden Nov 16, 2024
db3528a
Suggestions and prep for moving work to link.py
cwoffenden Nov 20, 2024
b14084f
Tabs to spaces
cwoffenden Nov 20, 2024
dff172c
Migrated the interactive tests to btest_exit
cwoffenden Nov 22, 2024
6b92ee4
Migrated the interactive tests to btest_exit
cwoffenden Nov 22, 2024
34a5422
Test audio files are needed, browser test needs to exit
cwoffenden Nov 22, 2024
49ad4d1
Comment reflects behaviour
cwoffenden Nov 27, 2024
90c4bae
Reverted assignments to the original order
cwoffenden Nov 27, 2024
8fd7016
Removed tests (to standalone PR #23394)
cwoffenden Jan 14, 2025
88ac28e
Merge branch 'main' into cw-audio-tweaks-3
cwoffenden Jan 16, 2025
ad0abee
Merge branch 'main' into cw-audio-optimised-copy
cwoffenden Jan 23, 2025
466a447
Work-in-progress 64-bit changes
cwoffenden Jan 24, 2025
659323a
Managed audio playback (if memory is less than 4GB)
cwoffenden Jan 24, 2025
8ea671d
Fully working with MEMORY64
cwoffenden Jan 27, 2025
2dfefb1
Extended tests to support MEMORY64 options
cwoffenden Jan 27, 2025
1d68bd1
Added audio tests with the stack starting at 4GB and 4GB
cwoffenden Jan 27, 2025
21f7761
Reduced initial memory size
cwoffenden Jan 27, 2025
54b171d
Merge branch 'main' into cw-audio-memory64
cwoffenden Jan 27, 2025
788d7cf
Added ruff suggestion of two lines
cwoffenden Jan 27, 2025
85ca54a
Merge branch 'main' into cw-audio-memory64
cwoffenden Jan 28, 2025
047223a
Interim commit just to get most of the CI failures running
cwoffenden Jan 29, 2025
8ad0c4a
Merge branch 'main' into cw-audio-memory64
cwoffenden Jan 29, 2025
bd13512
Files missed from previous commit...
cwoffenden Jan 29, 2025
11751cf
Removed magic numbers
cwoffenden Jan 29, 2025
68cdb19
Tidy
cwoffenden Jan 29, 2025
fa97e7d
Work in progress: WA creation now works (roughly) with addresses over…
cwoffenden Jan 29, 2025
1b36c8d
Merge branch 'main' into cw-audio-memory64
cwoffenden Jan 30, 2025
ccb12a0
Typo
cwoffenden Jan 30, 2025
568a86f
More work in progress
cwoffenden Jan 30, 2025
d218d23
Added ability to insert extra code in tests
cwoffenden Feb 3, 2025
4e1af38
Minor tidy (building with -Wextra)
cwoffenden Feb 3, 2025
bd5d653
Fixed the param offsets being totally wrong
cwoffenden Feb 3, 2025
48361fd
Initial mixer using audio params
cwoffenden Feb 3, 2025
37db147
Overhauled code and noted browser differences
cwoffenden Feb 4, 2025
b9a7742
Tidy, bullet-proof and document
cwoffenden Feb 5, 2025
8f9f6ad
Wrote a proper mixer
cwoffenden Feb 6, 2025
b0268c7
Minor: improved docs
cwoffenden Feb 6, 2025
92dc53e
Moved toIndex() helpers to the worklet
cwoffenden Feb 6, 2025
52ccabb
Merge branch 'main' into cw-audio-memory64
cwoffenden Feb 6, 2025
8597567
Added interactive parameter mixing test
cwoffenden Feb 6, 2025
1eba671
All interactive tests can now run with _2gb and 64_4gb modes
cwoffenden Feb 6, 2025
e2b64dc
ASSERTIONS found the stack overflow!
cwoffenden Feb 6, 2025
75da785
Merge branch 'main' into cw-audio-memory64
cwoffenden Feb 6, 2025
c600212
Ruf fix
cwoffenden Feb 6, 2025
e8e6395
More ruf fixes
cwoffenden Feb 6, 2025
a40a2d7
Merge branch 'main' into cw-audio-memory64
cwoffenden Feb 6, 2025
67bf288
Merge branch 'main' into cw-audio-memory64
cwoffenden Feb 6, 2025
178b9c5
Merge branch 'main' into cw-audio-memory64
cwoffenden Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 130 additions & 55 deletions src/audio_worklet.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,47 @@ function createWasmAudioWorkletProcessor(audioParams) {

// Capture the Wasm function callback to invoke.
let opts = args.processorOptions;
this.callbackFunction = Module['wasmTable'].get(opts['cb']);
this.userData = opts['ud'];
this.callbackFunction = Module['wasmTable'].get({{{ toIndexType("opts['cb']") }}});
this.userData = {{{ toIndexType("opts['ud']") }}};

// Then the samples per channel to process, fixed for the lifetime of the
// context that created this processor. Note for when moving to Web Audio
// 1.1: the typed array passed to process() should be the same size as this
// 'render quantum size', and this exercise of passing in the value
// shouldn't be required (to be verified).
// context that created this processor. Even though this 'render quantum
// size' is fixed at 128 samples in the 1.0 spec, it will be variable in
// the 1.1 spec. It's passed in now, just to prove it's settable, but will
// eventually be a property of the AudioWorkletGlobalScope (globalThis).
this.samplesPerChannel = opts['sc'];
this.bytesPerChannel = this.samplesPerChannel * {{{ getNativeTypeSize('float') }}};

// Create up-front as many typed views for marshalling the output data as
// may be required (with an arbitrary maximum of 10, for the case where a
// multi-MB stack is passed), allocated at the *top* of the worklet's
// stack (and whose addresses are fixed). The 'minimum alloc' firstly
// stops STACK_OVERFLOW_CHECK failing (since the stack will be full, and
// 16 being the minimum allocation size due to alignments) and leaves room
// for a single AudioSampleFrame as a minumum.
this.maxBuffers = Math.min(((Module['sz'] - /*minimum alloc*/ 16) / this.bytesPerChannel) | 0, /*sensible limit*/ 10);
#if ASSERTIONS
console.assert(this.maxBuffers > 0, `AudioWorklet needs more stack allocating (at least ${this.samplesPerChannel * 4})`);
#endif
// These are still alloc'd to take advantage of the overflow checks, etc.
var oldStackPtr = stackSave();
var viewDataIdx = {{{ getHeapOffset('stackAlloc(this.maxBuffers * this.bytesPerChannel)', 'float') }}};
#if WEBAUDIO_DEBUG
console.log(`AudioWorklet creating ${this.maxBuffers} buffer one-time views (for a stack size of ${Module['sz']} at address 0x${(viewDataIdx * 4).toString(16)})`);
#endif
this.outputViews = [];
for (var i = this.maxBuffers; i > 0; i--) {
// Added in reverse so the lowest indices are closest to the stack top
this.outputViews.unshift(
HEAPF32.subarray(viewDataIdx, viewDataIdx += this.samplesPerChannel)
);
}
stackRestore(oldStackPtr);

#if ASSERTIONS
// Explicitly verify this later in process()
this.ctorOldStackPtr = oldStackPtr;
#endif
}

static get parameterDescriptors() {
Expand All @@ -51,75 +84,122 @@ function createWasmAudioWorkletProcessor(audioParams) {
let numInputs = inputList.length,
numOutputs = outputList.length,
numParams = 0, i, j, k, dataPtr,
bytesPerChannel = this.samplesPerChannel * 4,
outputViewsNeeded = 0,
stackMemoryNeeded = (numInputs + numOutputs) * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}},
oldStackPtr = stackSave(),
inputsPtr, outputsPtr, outputDataPtr, paramsPtr,
inputsPtr, outputsPtr, paramsPtr,
didProduceAudio, paramArray;

// Calculate how much stack space is needed.
for (i of inputList) stackMemoryNeeded += i.length * bytesPerChannel;
for (i of outputList) stackMemoryNeeded += i.length * bytesPerChannel;
// Calculate how much stack space is needed
for (i of inputList) stackMemoryNeeded += i.length * this.bytesPerChannel;
for (i of outputList) outputViewsNeeded += i.length;
stackMemoryNeeded += outputViewsNeeded * this.bytesPerChannel;
for (i in parameters) stackMemoryNeeded += parameters[i].byteLength + {{{ C_STRUCTS.AudioParamFrame.__size__ }}}, ++numParams;

// Allocate the necessary stack space.
inputsPtr = stackAlloc(stackMemoryNeeded);
#if ASSERTIONS
console.assert(oldStackPtr == this.ctorOldStackPtr, 'AudioWorklet stack address has unexpectedly moved');
console.assert(outputViewsNeeded <= this.outputViews.length, `Too many AudioWorklet outputs (need ${outputViewsNeeded} but have stack space for ${this.outputViews.length})`);
#endif

// Allocate the necessary stack space (dataPtr is always in bytes, and
// advances as space for structs and data is taken, but note the switching
// between bytes and indices into the various heaps, usually in 'k'). This
// will be 16-byte aligned (from _emscripten_stack_alloc()), as were the
// output views, so we round up and advance the required bytes to ensure
// the addresses all work out at the end.
i = (stackMemoryNeeded + 15) & ~15;
dataPtr = stackAlloc(i) + (i - stackMemoryNeeded);

// Copy input audio descriptor structs and data to Wasm
k = inputsPtr >> 2;
dataPtr = inputsPtr + numInputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
// Note: filling the structs was tried with makeSetValue() but it creates
// minor overhead (adds and shifts) that we can avoid (and no combination
// of optimisations will fold).
inputsPtr = dataPtr;
k = {{{ getHeapOffset('inputsPtr', 'u32') }}};
dataPtr += numInputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
for (i of inputList) {
// Write the AudioSampleFrame struct instance
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr;
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / getNativeTypeSize('u32') }}}] = i.length;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / getNativeTypeSize('u32') }}}] = this.samplesPerChannel;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / getNativeTypeSize('u32') }}}] = dataPtr;
#if MEMORY64
// See the note in the constructor for dealing with 64-bit addresses
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / getNativeTypeSize('u32') + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / getNativeTypeSize('u32') }}};
// Marshal the input audio sample data for each audio channel of this input
for (j of i) {
HEAPF32.set(j, dataPtr>>2);
dataPtr += bytesPerChannel;
HEAPF32.set(j, {{{ getHeapOffset('dataPtr', 'float') }}});
dataPtr += this.bytesPerChannel;
}
}

// Copy output audio descriptor structs to Wasm
outputsPtr = dataPtr;
k = outputsPtr >> 2;
outputDataPtr = (dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}) >> 2;
for (i of outputList) {
// Write the AudioSampleFrame struct instance
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr;
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
// Reserve space for the output data
dataPtr += bytesPerChannel * i.length;
}

// Copy parameters descriptor structs and data to Wasm
paramsPtr = dataPtr;
k = paramsPtr >> 2;
k = {{{ getHeapOffset('paramsPtr', 'u32') }}};
dataPtr += numParams * {{{ C_STRUCTS.AudioParamFrame.__size__ }}};
for (i = 0; paramArray = parameters[i++];) {
// Write the AudioParamFrame struct instance
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.length / 4 }}}] = paramArray.length;
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.data / 4 }}}] = dataPtr;
k += {{{ C_STRUCTS.AudioParamFrame.__size__ / 4 }}};
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.length / getNativeTypeSize('u32') }}}] = paramArray.length;
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.data / getNativeTypeSize('u32') }}}] = dataPtr;
#if MEMORY64
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / getNativeTypeSize('u32') + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioParamFrame.__size__ / getNativeTypeSize('u32') }}};
// Marshal the audio parameters array
HEAPF32.set(paramArray, dataPtr>>2);
dataPtr += paramArray.length*4;
HEAPF32.set(paramArray, {{{ getHeapOffset('dataPtr', 'float') }}});
dataPtr += paramArray.length * {{{ getNativeTypeSize('float') }}};
}

// Copy output audio descriptor structs to Wasm (note that dataPtr after
// the struct offsets should now be 16-byte aligned).
outputsPtr = dataPtr;
k = {{{ getHeapOffset('outputsPtr', 'u32') }}};
dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
for (i of outputList) {
// Write the AudioSampleFrame struct instance
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / getNativeTypeSize('u32') }}}] = i.length;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / getNativeTypeSize('u32') }}}] = this.samplesPerChannel;
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / getNativeTypeSize('u32') }}}] = dataPtr;
#if MEMORY64
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / getNativeTypeSize('u32') + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / getNativeTypeSize('u32') }}};
// Advance the output pointer to the next output (matching the pre-allocated views)
dataPtr += this.bytesPerChannel * i.length;
}

#if ASSERTIONS
// If all the maths worked out, we arrived at the original stack address
console.assert(dataPtr == oldStackPtr, `AudioWorklet stack missmatch (audio data finishes at ${dataPtr} instead of ${oldStackPtr})`);

// Sanity checks. If these trip the most likely cause, beyond unforeseen
// stack shenanigans, is that the 'render quantum size' changed.
if (numOutputs) {
// First that the output view addresses match the stack positions.
k = dataPtr - this.bytesPerChannel;
for (i = 0; i < outputViewsNeeded; i++) {
console.assert(k == this.outputViews[i].byteOffset, 'AudioWorklet internal error in addresses of the output array views');
k -= this.bytesPerChannel;
}
// And that the views' size match the passed in output buffers
for (i of outputList) {
for (j of i) {
console.assert(j.byteLength == this.bytesPerChannel, `AudioWorklet unexpected output buffer size (expected ${this.bytesPerChannel} got ${j.byteLength})`);
}
}
}
#endif

// Call out to Wasm callback to perform audio processing
if (didProduceAudio = this.callbackFunction(numInputs, inputsPtr, numOutputs, outputsPtr, numParams, paramsPtr, this.userData)) {
if (didProduceAudio = this.callbackFunction(numInputs, {{{ toIndexType('inputsPtr') }}}, numOutputs, {{{ toIndexType('outputsPtr') }}}, numParams, {{{ toIndexType('paramsPtr') }}}, this.userData)) {
// Read back the produced audio data to all outputs and their channels.
// (A garbage-free function TypedArray.copy(dstTypedArray, dstOffset,
// srcTypedArray, srcOffset, count) would sure be handy.. but web does
// not have one, so manually copy all bytes in)
// The preallocated 'outputViews' already have the correct offsets and
// sizes into the stack (recall from the ctor that they run backwards).
k = outputViewsNeeded - 1;
for (i of outputList) {
for (j of i) {
for (k = 0; k < this.samplesPerChannel; ++k) {
j[k] = HEAPF32[outputDataPtr++];
}
j.set(this.outputViews[k--]);
}
}
}
Expand Down Expand Up @@ -193,14 +273,9 @@ class BootstrapMessages extends AudioWorkletProcessor {
// 'cb' the callback function
// 'ch' the context handle
// 'ud' the passed user data
p.postMessage({'_wsc': d['cb'], 'x': [d['ch'], 1/*EM_TRUE*/, d['ud']] });
p.postMessage({'_wsc': {{{ toIndexType("d['cb']") }}}, 'x': [d['ch'], 1/*EM_TRUE*/, {{{ toIndexType("d['ud']") }}}] });
} else if (d['_wsc']) {
#if MEMORY64
var ptr = BigInt(d['_wsc']);
#else
var ptr = d['_wsc'];
#endif
Module['wasmTable'].get(ptr)(...d['x']);
Module['wasmTable'].get({{{ toIndexType("d['_wsc']") }}})(...d['x']);
};
}
}
Expand Down
45 changes: 27 additions & 18 deletions src/lib/libwebaudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ let LibraryWebAudio = {
#if WEBAUDIO_DEBUG
console.log(`emscripten_resume_audio_context_async() callback: New audio state="${EmAudio[contextHandle].state}", ID=${state}`);
#endif
{{{ makeDynCall('viii', 'callback') }}}(contextHandle, state, userData);
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, state, userData);
}
#if WEBAUDIO_DEBUG
console.log(`emscripten_resume_audio_context_async() resuming...`);
Expand Down Expand Up @@ -162,9 +162,13 @@ let LibraryWebAudio = {
console.log(`emscripten_start_wasm_audio_worklet_thread_async() adding audioworklet.js...`);
#endif

let audioWorkletCreationFailed = () => {
let audioWorkletCreationFailed = (err) => {
#if WEBAUDIO_DEBUG
console.error(`emscripten_start_wasm_audio_worklet_thread_async() addModule() failed!`);
// Note about Cross-Origin here: a lack of Cross-Origin-Opener-Policy and
// Cross-Origin-Embedder-Policy headers to the client request will result
// in the worklet file failing to load.
console.error(`emscripten_start_wasm_audio_worklet_thread_async() addModule() failed! Are the Cross-Origin headers being set?`);
if (err) console.error(err);
#endif
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, 0/*EM_FALSE*/, userData);
};
Expand All @@ -178,7 +182,7 @@ let LibraryWebAudio = {
console.error(`AudioWorklets are not supported by current browser.`);
}
#endif
return audioWorkletCreationFailed();
return audioWorkletCreationFailed(null);
}

// TODO: In MINIMAL_RUNTIME builds, read this file off of a preloaded Blob,
Expand Down Expand Up @@ -222,7 +226,7 @@ let LibraryWebAudio = {
#if WEBAUDIO_DEBUG
console.log(`emscripten_start_wasm_audio_worklet_thread_async() addModule() of main application JS completed`);
#endif
{{{ makeDynCall('viii', 'callback') }}}(contextHandle, 1/*EM_TRUE*/, userData);
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, 1/*EM_TRUE*/, userData);
}).catch(audioWorkletCreationFailed);
},

Expand All @@ -241,32 +245,35 @@ let LibraryWebAudio = {
assert(EmAudio[contextHandle] instanceof (window.AudioContext || window.webkitAudioContext), `Called emscripten_create_wasm_audio_worklet_processor_async() on a context handle ${contextHandle} that is not an AudioContext, but of type ${typeof EmAudio[contextHandle]}`);
#endif

options >>= 2;
let audioParams = [],
numAudioParams = HEAPU32[options+1],
audioParamDescriptors = HEAPU32[options+2] >> 2,
processorName = UTF8ToString({{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.name, '*') }}}),
numAudioParams = {{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.numAudioParams, 'i32') }}},
audioParamDescriptors = {{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.audioParamDescriptors, '*') }}},
i = 0;

while (numAudioParams--) {
audioParams.push({
name: i++,
defaultValue: HEAPF32[audioParamDescriptors++],
minValue: HEAPF32[audioParamDescriptors++],
maxValue: HEAPF32[audioParamDescriptors++],
automationRate: ['a','k'][HEAPU32[audioParamDescriptors++]] + '-rate',
defaultValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.defaultValue, 'float') }}},
minValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.minValue, 'float') }}},
maxValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.maxValue, 'float') }}},
automationRate: ({{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.automationRate, 'i32') }}} ? 'k' : 'a') + '-rate'
});
audioParamDescriptors += {{{ C_STRUCTS.WebAudioParamDescriptor.__size__ }}};
}

#if WEBAUDIO_DEBUG
console.log(`emscripten_create_wasm_audio_worklet_processor_async() creating a new AudioWorklet processor with name ${UTF8ToString(HEAPU32[options])}`);
console.log(`emscripten_create_wasm_audio_worklet_processor_async() creating a new AudioWorklet processor with name ${processorName}`);
#endif

EmAudio[contextHandle].audioWorklet.bootstrapMessage.port.postMessage({
// Deliberately mangled and short names used here ('_wpn', the 'Worklet
// Processor Name' used as a 'key' to verify the message type so as to
// not get accidentally mixed with user submitted messages, the remainder
// for space saving reasons, abbreviated from their variable names).
'_wpn': UTF8ToString(HEAPU32[options]),
// Note: we can only pass clonable object, so need to pass the function
// pointer and not the wasm function object.
'_wpn': processorName,
'ap': audioParams,
'ch': contextHandle,
'cb': callback,
Expand All @@ -281,18 +288,20 @@ let LibraryWebAudio = {
assert(EmAudio[contextHandle], `Called emscripten_create_wasm_audio_worklet_node() with a nonexisting/already freed Web Audio Context handle ${contextHandle}!`);
assert(EmAudio[contextHandle] instanceof (window.AudioContext || window.webkitAudioContext), `Called emscripten_create_wasm_audio_worklet_node() on a context handle ${contextHandle} that is not an AudioContext, but of type ${typeof EmAudio[contextHandle]}`);
#endif
options >>= 2;

function readChannelCountArray(heapIndex, numOutputs) {
if (!heapIndex) return void 0;
heapIndex = {{{ getHeapOffset('heapIndex', 'i32') }}};
let channelCounts = [];
while (numOutputs--) channelCounts.push(HEAPU32[heapIndex++]);
return channelCounts;
}

let optionsOutputs = options ? {{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.numberOfOutputs, 'i32') }}} : 0;
let opts = options ? {
numberOfInputs: HEAP32[options],
numberOfOutputs: HEAP32[options+1],
outputChannelCount: HEAPU32[options+2] ? readChannelCountArray(HEAPU32[options+2]>>2, HEAP32[options+1]) : void 0,
numberOfInputs: {{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.numberOfInputs, 'i32') }}},
numberOfOutputs: optionsOutputs,
outputChannelCount: readChannelCountArray({{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.outputChannelCounts, 'i32*') }}}, optionsOutputs),
processorOptions: {
'cb': callback,
'ud': userData,
Expand Down
Loading