Skip to content

Commit 1b430a3

Browse files
authored
[AUDIO_WORKLET] Add support for MEMORY64 with 2GB and 4GB heap (#24732)
This pulls the changes out of #23508 to add just the `MEMORY64` support. The already merged interactive tests (from #23659) can be run with: ``` test/runner.py interactive_2gb.test_audio_worklet_params_mixing test/runner.py interactive64_4gb.test_audio_worklet_params_mixing ``` The browser tests with wasm64 have been re-enabled. Note: the intention is to add the performance improvements (from last October) once this is merged (which are still relevant and produce the same improvements).
1 parent 3ec2875 commit 1b430a3

9 files changed

+245
-189
lines changed

src/audio_worklet.js

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function createWasmAudioWorkletProcessor(audioParams) {
2525
assert(opts.callback)
2626
assert(opts.samplesPerChannel)
2727
#endif
28-
this.callback = getWasmTableEntry(opts.callback);
28+
this.callback = {{{ makeDynCall('iipipipp', 'opts.callback') }}};
2929
this.userData = opts.userData;
3030
// Then the samples per channel to process, fixed for the lifetime of the
3131
// context that created this processor. Note for when moving to Web Audio
@@ -65,48 +65,48 @@ function createWasmAudioWorkletProcessor(audioParams) {
6565
inputsPtr = stackAlloc(stackMemoryNeeded);
6666

6767
// Copy input audio descriptor structs and data to Wasm
68-
k = inputsPtr >> 2;
68+
k = inputsPtr;
6969
dataPtr = inputsPtr + numInputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
7070
for (i of inputList) {
7171
// Write the AudioSampleFrame struct instance
72-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
73-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel;
74-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr;
75-
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
72+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.numberOfChannels, 'i.length', 'u32') }}};
73+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.samplesPerChannel, 'this.samplesPerChannel', 'u32') }}};
74+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.data, 'dataPtr', '*') }}};
75+
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
7676
// Marshal the input audio sample data for each audio channel of this input
7777
for (j of i) {
78-
HEAPF32.set(j, dataPtr>>2);
78+
HEAPF32.set(j, {{{ getHeapOffset('dataPtr', 'float') }}});
7979
dataPtr += bytesPerChannel;
8080
}
8181
}
8282

8383
// Copy output audio descriptor structs to Wasm
8484
outputsPtr = dataPtr;
85-
k = outputsPtr >> 2;
86-
outputDataPtr = (dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}) >> 2;
85+
k = outputsPtr;
86+
outputDataPtr = (dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}});
8787
for (i of outputList) {
8888
// Write the AudioSampleFrame struct instance
89-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
90-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel;
91-
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr;
92-
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
89+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.numberOfChannels, 'i.length', 'u32') }}};
90+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.samplesPerChannel, 'this.samplesPerChannel', 'u32') }}};
91+
{{{ makeSetValue('k', C_STRUCTS.AudioSampleFrame.data, 'dataPtr', '*') }}};
92+
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
9393
// Reserve space for the output data
9494
dataPtr += bytesPerChannel * i.length;
9595
}
9696

9797
// Copy parameters descriptor structs and data to Wasm
9898
paramsPtr = dataPtr;
99-
k = paramsPtr >> 2;
99+
k = paramsPtr;
100100
dataPtr += numParams * {{{ C_STRUCTS.AudioParamFrame.__size__ }}};
101101

102102
for (i = 0; paramArray = parameters[i++];) {
103103
// Write the AudioParamFrame struct instance
104-
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.length / 4 }}}] = paramArray.length;
105-
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.data / 4 }}}] = dataPtr;
106-
k += {{{ C_STRUCTS.AudioParamFrame.__size__ / 4 }}};
104+
{{{ makeSetValue('k', C_STRUCTS.AudioParamFrame.length, 'paramArray.length', 'u32') }}};
105+
{{{ makeSetValue('k', C_STRUCTS.AudioParamFrame.data, 'dataPtr', '*') }}};
106+
k += {{{ C_STRUCTS.AudioParamFrame.__size__ }}};
107107
// Marshal the audio parameters array
108-
HEAPF32.set(paramArray, dataPtr>>2);
109-
dataPtr += paramArray.length*4;
108+
HEAPF32.set(paramArray, {{{ getHeapOffset('dataPtr', 'float') }}});
109+
dataPtr += paramArray.length * {{{ getNativeTypeSize('float') }}};
110110
}
111111

112112
// Call out to Wasm callback to perform audio processing
@@ -115,6 +115,7 @@ function createWasmAudioWorkletProcessor(audioParams) {
115115
// (A garbage-free function TypedArray.copy(dstTypedArray, dstOffset,
116116
// srcTypedArray, srcOffset, count) would sure be handy.. but web does
117117
// not have one, so manually copy all bytes in)
118+
outputDataPtr = {{{ getHeapOffset('outputDataPtr', 'float') }}};
118119
for (i of outputList) {
119120
for (j of i) {
120121
for (k = 0; k < this.samplesPerChannel; ++k) {
@@ -167,8 +168,12 @@ class BootstrapMessages extends AudioWorkletProcessor {
167168
// of the emscripten_create_wasm_audio_worklet_processor_async() call.
168169
//
169170
// '_wsc' is short for 'wasm call', using an identifier that will never
170-
// conflict with user messages
171-
messagePort.postMessage({'_wsc': d.callback, args: [d.contextHandle, 1/*EM_TRUE*/, d.userData] });
171+
// conflict with user messages.
172+
//
173+
// Note: we convert the pointer arg manually here since the call site
174+
// ($_EmAudioDispatchProcessorCallback) is used with various signatures
175+
// and we do not know the types in advance.
176+
messagePort.postMessage({'_wsc': d.callback, args: [d.contextHandle, 1/*EM_TRUE*/, {{{ to64('d.userData') }}}] });
172177
} else if (d['_wsc']) {
173178
getWasmTableEntry(d['_wsc'])(...d.args);
174179
};

src/lib/libwebaudio.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ var LibraryWebAudio = {
7878
#if WEBAUDIO_DEBUG
7979
console.log(`emscripten_resume_audio_context_async() callback: New audio state="${EmAudio[contextHandle].state}", ID=${state}`);
8080
#endif
81-
{{{ makeDynCall('viii', 'callback') }}}(contextHandle, state, userData);
81+
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, state, userData);
8282
}
8383
#if WEBAUDIO_DEBUG
8484
console.log(`emscripten_resume_audio_context_async() resuming...`);
@@ -202,15 +202,16 @@ var LibraryWebAudio = {
202202
}
203203
});
204204
audioWorklet.bootstrapMessage.port.onmessage = _EmAudioDispatchProcessorCallback;
205-
{{{ makeDynCall('viii', 'callback') }}}(contextHandle, 1/*EM_TRUE*/, userData);
205+
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, 1/*EM_TRUE*/, userData);
206206
}).catch(audioWorkletCreationFailed);
207207
},
208208

209209
$_EmAudioDispatchProcessorCallback__deps: ['$getWasmTableEntry'],
210210
$_EmAudioDispatchProcessorCallback: (e) => {
211211
var data = e.data;
212212
// '_wsc' is short for 'wasm call', trying to use an identifier name that
213-
// will never conflict with user code
213+
// will never conflict with user code. This is used to call both the 3-param
214+
// call (handle, true, userData) and the variable argument post functions.
214215
var wasmCall = data['_wsc'];
215216
wasmCall && getWasmTableEntry(wasmCall)(...data.args);
216217
},
@@ -222,32 +223,33 @@ var LibraryWebAudio = {
222223
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]}`);
223224
#endif
224225

225-
options >>= 2;
226-
var audioParams = [],
227-
numAudioParams = HEAPU32[options+1],
228-
audioParamDescriptors = HEAPU32[options+2] >> 2,
229-
i = 0;
226+
var audioParams = [];
227+
var processorName = UTF8ToString({{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.name, '*') }}});
228+
var numAudioParams = {{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.numAudioParams, 'i32') }}};
229+
var audioParamDescriptors = {{{ makeGetValue('options', C_STRUCTS.WebAudioWorkletProcessorCreateOptions.audioParamDescriptors, '*') }}};
230+
var paramIndex = 0;
230231

231232
while (numAudioParams--) {
232233
audioParams.push({
233-
name: i++,
234-
defaultValue: HEAPF32[audioParamDescriptors++],
235-
minValue: HEAPF32[audioParamDescriptors++],
236-
maxValue: HEAPF32[audioParamDescriptors++],
237-
automationRate: ['a','k'][HEAPU32[audioParamDescriptors++]] + '-rate',
234+
name: paramIndex++,
235+
defaultValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.defaultValue, 'float') }}},
236+
minValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.minValue, 'float') }}},
237+
maxValue: {{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.maxValue, 'float') }}},
238+
automationRate: ({{{ makeGetValue('audioParamDescriptors', C_STRUCTS.WebAudioParamDescriptor.automationRate, 'i32') }}} ? 'k' : 'a') + '-rate',
238239
});
240+
audioParamDescriptors += {{{ C_STRUCTS.WebAudioParamDescriptor.__size__ }}};
239241
}
240242

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

245247
EmAudio[contextHandle].audioWorklet.bootstrapMessage.port.postMessage({
246248
// Deliberately mangled and short names used here ('_wpn', the 'Worklet
247249
// Processor Name' used as a 'key' to verify the message type so as to
248250
// not get accidentally mixed with user submitted messages, the remainder
249251
// for space saving reasons, abbreviated from their variable names).
250-
'_wpn': UTF8ToString(HEAPU32[options]),
252+
'_wpn': processorName,
251253
audioParams,
252254
contextHandle,
253255
callback,
@@ -262,18 +264,20 @@ var LibraryWebAudio = {
262264
assert(EmAudio[contextHandle], `Called emscripten_create_wasm_audio_worklet_node() with a nonexisting/already freed Web Audio Context handle ${contextHandle}!`);
263265
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]}`);
264266
#endif
265-
options >>= 2;
266267

267268
function readChannelCountArray(heapIndex, numOutputs) {
269+
if (!heapIndex) return undefined;
270+
heapIndex = {{{ getHeapOffset('heapIndex', 'i32') }}};
268271
var channelCounts = [];
269272
while (numOutputs--) channelCounts.push(HEAPU32[heapIndex++]);
270273
return channelCounts;
271274
}
272275

276+
var optionsOutputs = options ? {{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.numberOfOutputs, 'i32') }}} : 0;
273277
var opts = options ? {
274-
numberOfInputs: HEAP32[options],
275-
numberOfOutputs: HEAP32[options+1],
276-
outputChannelCount: HEAPU32[options+2] ? readChannelCountArray(HEAPU32[options+2]>>2, HEAP32[options+1]) : undefined,
278+
numberOfInputs: {{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.numberOfInputs, 'i32') }}},
279+
numberOfOutputs: optionsOutputs,
280+
outputChannelCount: readChannelCountArray({{{ makeGetValue('options', C_STRUCTS.EmscriptenAudioWorkletNodeCreateOptions.outputChannelCounts, 'i32*') }}}, optionsOutputs),
277281
processorOptions: {
278282
callback,
279283
userData,

src/struct_info.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,17 @@
12661266
{
12671267
"file": "emscripten/webaudio.h",
12681268
"structs": {
1269+
"WebAudioParamDescriptor": [
1270+
"defaultValue",
1271+
"minValue",
1272+
"maxValue",
1273+
"automationRate"
1274+
],
1275+
"WebAudioWorkletProcessorCreateOptions": [
1276+
"name",
1277+
"numAudioParams",
1278+
"audioParamDescriptors"
1279+
],
12691280
"AudioSampleFrame": [
12701281
"numberOfChannels",
12711282
"samplesPerChannel",
@@ -1274,6 +1285,11 @@
12741285
"AudioParamFrame": [
12751286
"length",
12761287
"data"
1288+
],
1289+
"EmscriptenAudioWorkletNodeCreateOptions": [
1290+
"numberOfInputs",
1291+
"numberOfOutputs",
1292+
"outputChannelCounts"
12771293
]
12781294
}
12791295
},

src/struct_info_generated.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,12 @@
534534
"numberOfChannels": 0,
535535
"samplesPerChannel": 4
536536
},
537+
"EmscriptenAudioWorkletNodeCreateOptions": {
538+
"__size__": 12,
539+
"numberOfInputs": 0,
540+
"numberOfOutputs": 4,
541+
"outputChannelCounts": 8
542+
},
537543
"EmscriptenBatteryEvent": {
538544
"__size__": 32,
539545
"charging": 24,
@@ -1479,6 +1485,19 @@
14791485
"module": 4,
14801486
"nextInChain": 0
14811487
},
1488+
"WebAudioParamDescriptor": {
1489+
"__size__": 16,
1490+
"automationRate": 12,
1491+
"defaultValue": 0,
1492+
"maxValue": 8,
1493+
"minValue": 4
1494+
},
1495+
"WebAudioWorkletProcessorCreateOptions": {
1496+
"__size__": 12,
1497+
"audioParamDescriptors": 8,
1498+
"name": 0,
1499+
"numAudioParams": 4
1500+
},
14821501
"__cxa_exception": {
14831502
"__size__": 24,
14841503
"adjustedPtr": 16,

src/struct_info_generated_wasm64.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,12 @@
534534
"numberOfChannels": 0,
535535
"samplesPerChannel": 4
536536
},
537+
"EmscriptenAudioWorkletNodeCreateOptions": {
538+
"__size__": 16,
539+
"numberOfInputs": 0,
540+
"numberOfOutputs": 4,
541+
"outputChannelCounts": 8
542+
},
537543
"EmscriptenBatteryEvent": {
538544
"__size__": 32,
539545
"charging": 24,
@@ -1479,6 +1485,19 @@
14791485
"module": 8,
14801486
"nextInChain": 0
14811487
},
1488+
"WebAudioParamDescriptor": {
1489+
"__size__": 16,
1490+
"automationRate": 12,
1491+
"defaultValue": 0,
1492+
"maxValue": 8,
1493+
"minValue": 4
1494+
},
1495+
"WebAudioWorkletProcessorCreateOptions": {
1496+
"__size__": 24,
1497+
"audioParamDescriptors": 16,
1498+
"name": 0,
1499+
"numAudioParams": 8
1500+
},
14821501
"__cxa_exception": {
14831502
"__size__": 48,
14841503
"adjustedPtr": 32,

0 commit comments

Comments
 (0)