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 48 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
158 changes: 117 additions & 41 deletions src/audio_worklet.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,48 @@ function createWasmAudioWorkletProcessor(audioParams) {
let opts = args.processorOptions;
this.callbackFunction = Module['wasmTable'].get(opts['cb']);
this.userData = 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'];

// 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.
// Note: here and in the rest of the code the natural '>>> 2' unsigned
// shifts for bytes to HEAPU32 offsets have been replaced with '/ 4',
// otherwise the values are truncated to 32-bit addresses, which fails
// when compiling with MEMORY64.
this.maxBuffers = Math.min(((Module['sz'] - /*minimum alloc*/ 16) / (this.samplesPerChannel * 4)) | 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 = stackAlloc(this.maxBuffers * this.samplesPerChannel * 4) / 4;
#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 @@ -52,74 +88,119 @@ function createWasmAudioWorkletProcessor(audioParams) {
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.
// Calculate how much stack space is needed
for (i of inputList) stackMemoryNeeded += i.length * bytesPerChannel;
for (i of outputList) stackMemoryNeeded += i.length * bytesPerChannel;
for (i of outputList) outputViewsNeeded += i.length;
stackMemoryNeeded += outputViewsNeeded * 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__ }}};
inputsPtr = dataPtr;
k = inputsPtr / 4;
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;
#if MEMORY64
// See the note in the constructor for dealing with 64-bit addresses
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
// Marshal the input audio sample data for each audio channel of this input
for (j of i) {
HEAPF32.set(j, dataPtr>>2);
HEAPF32.set(j, dataPtr / 4);
dataPtr += bytesPerChannel;
}
}

// Copy output audio descriptor structs to Wasm
// Copy parameters descriptor structs and data to Wasm
paramsPtr = dataPtr;
k = paramsPtr / 4;
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;
#if MEMORY64
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioParamFrame.__size__ / 4 }}};
// Marshal the audio parameters array
HEAPF32.set(paramArray, dataPtr / 4);
dataPtr += paramArray.length * 4;
}

// Copy output audio descriptor structs to Wasm (note that dataPtr after
// the struct offsets should now be 16-byte aligned).
outputsPtr = dataPtr;
k = outputsPtr >> 2;
outputDataPtr = (dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}) >> 2;
k = outputsPtr / 4;
dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
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;
#if MEMORY64
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 + 1 }}}] = dataPtr / 0x100000000;
#endif
k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}};
// Reserve space for the output data
// Advance the output pointer to the next output (matching the pre-allocated views)
dataPtr += bytesPerChannel * i.length;
}

// Copy parameters descriptor structs and data to Wasm
paramsPtr = dataPtr;
k = paramsPtr >> 2;
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 }}};
// Marshal the audio parameters array
HEAPF32.set(paramArray, dataPtr>>2);
dataPtr += paramArray.length*4;
#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 - 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 -= bytesPerChannel;
}
// And that the views' size match the passed in output buffers
for (i of outputList) {
for (j of i) {
console.assert(j.byteLength == bytesPerChannel, `AudioWorklet unexpected output buffer size (expected ${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 @@ -195,12 +276,7 @@ class BootstrapMessages extends AudioWorkletProcessor {
// 'ud' the passed user data
p.postMessage({'_wsc': d['cb'], 'x': [d['ch'], 1/*EM_TRUE*/, 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
24 changes: 15 additions & 9 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 Down Expand Up @@ -266,11 +270,13 @@ let LibraryWebAudio = {
// 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).
// Note: we can only pass clonable object, so need to pass the function
// pointer and not the wasm function object.
'_wpn': UTF8ToString(HEAPU32[options]),
'ap': audioParams,
'ch': contextHandle,
'cb': callback,
'ud': userData
'cb': {{{ toIndexType('callback') }}},
'ud': {{{ toIndexType('userData') }}}
});
},

Expand All @@ -294,8 +300,8 @@ let LibraryWebAudio = {
numberOfOutputs: HEAP32[options+1],
outputChannelCount: HEAPU32[options+2] ? readChannelCountArray(HEAPU32[options+2]>>2, HEAP32[options+1]) : void 0,
processorOptions: {
'cb': callback,
'ud': userData,
'cb': {{{ toIndexType('callback') }}},
cwoffenden marked this conversation as resolved.
Show resolved Hide resolved
'ud': {{{ toIndexType('userData') }}},
cwoffenden marked this conversation as resolved.
Show resolved Hide resolved
'sc': emscriptenGetContextQuantumSize(contextHandle)
}
} : void 0;
Expand Down
14 changes: 14 additions & 0 deletions test/test_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ def test_audio_worklet_2x_hard_pan_io(self):
shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/')
self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])

# Same as 'test_audio_worklet_2x_stereo_io' but as wasm64 with the worklet stack starting 2GB in (tests for unsigned shifts in the memory offsets)
def test_audio_worklet_stereo_io_2gb(self):
os.mkdir('audio_files')
shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/')
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-sMEMORY64', '-sINITIAL_MEMORY=4200mb', '-DTEST_OFFSET_GB=2'])

# Same as 'test_audio_worklet_stereo_io_2gb' but with a 4GB offset
def test_audio_worklet_stereo_io_4gb(self):
os.mkdir('audio_files')
shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/')
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-sMEMORY64', '-sINITIAL_MEMORY=4200mb', '-DTEST_OFFSET_GB=4'])


class interactive64(interactive):
def setUp(self):
Expand Down
46 changes: 43 additions & 3 deletions test/webaudio/audioworklet_test_shared.inc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ EM_JS(EMSCRIPTEN_WEBAUDIO_T, createTrack, (EMSCRIPTEN_WEBAUDIO_T ctxID, const ch
var context = emscriptenGetAudioObject(ctxID);
if (context) {
var audio = document.createElement('audio');
#if __wasm64__
// Workaround for UTF8ToString() needing a JS number and from64() not working in EM_JS
url = Number(url);
#endif
audio.src = UTF8ToString(url);
audio.loop = looping;
var track = context.createMediaElementSource(audio);
Expand Down Expand Up @@ -82,14 +86,50 @@ void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
emscripten_create_wasm_audio_worklet_processor_async(context, &opts, &processorCreated, NULL);
}

// Common entry point for the mixer tests
// stackSize - audio worklet stack size in bytes
// return true if the requested memory could be allocated
bool _main_(int stackSize) {
// Optional empty space before the audio worklet's stack
#if TEST_OFFSET_GB > 0
char* const emptySpace = malloc(1073741824L * TEST_OFFSET_GB);
if (emptySpace) {
cwoffenden marked this conversation as resolved.
Show resolved Hide resolved
printf("Empty space allocated (%dGB)\n", TEST_OFFSET_GB);
} else {
printf("Failed to allocate the required memory offset\n");
return false;
}
#endif

char* const workletStack = memalign(16, stackSize);
if (!workletStack) {
cwoffenden marked this conversation as resolved.
Show resolved Hide resolved
printf("Failed to allocate the required worklet stack\n");
return false;
}

#if TEST_OFFSET_GB > 0
free(emptySpace);
#endif

EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(NULL);
emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, stackSize, &initialised, NULL);
#ifndef BROWSER_TEST
// Special case: browser tests need to exit instantly, interactive tests need to wait
emscripten_runtime_keepalive_push();
#endif
return true;
}

// Common entry point for the mixer tests
int main() {
static char workletStack[AUDIO_STACK_SIZE];
char* const workletStack = memalign(16, AUDIO_STACK_SIZE);
printf("Audio worklet stack at 0x%p\n", workletStack);
assert(workletStack);
EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(NULL);
emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, sizeof workletStack, &initialised, NULL);
emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, AUDIO_STACK_SIZE, &initialised, NULL);
#ifndef BROWSER_TEST
// Special case: browser tests need to exit instantly, interactive tests need to wait
emscripten_runtime_keepalive_push();
#endif
return 0;
return EXIT_SUCCESS;
}
Loading