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

[test] AudioWorklet tests now correctly run and exit #23695

Merged
merged 17 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 10 additions & 5 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5459,7 +5459,6 @@ def test_full_js_library_strict(self):
# Tests the AudioWorklet demo
@parameterized({
'': ([],),
'memory64': (['-sMEMORY64'],),
'with_fs': (['--preload-file', test_file('hello_world.c') + '@/'],),
'closure': (['--closure', '1', '-Oz'],),
'asyncify': (['-sASYNCIFY'],),
Expand All @@ -5471,29 +5470,35 @@ def test_full_js_library_strict(self):
'es6': (['-sEXPORT_ES6'],),
'strict': (['-sSTRICT'],),
})
@no_wasm64('https://github.com/emscripten-core/emscripten/pull/23508')
@no_2gb('https://github.com/emscripten-core/emscripten/pull/23508')
@requires_sound_hardware
def test_audio_worklet(self, args):
self.btest_exit('webaudio/audioworklet.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'] + args)
self.btest_exit('webaudio/audioworklet.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT'] + args)

# Tests that audioworklets and workers can be used at the same time
# Note: doesn't need audio hardware (and has no AW code that tests 2GB or wasm64)
def test_audio_worklet_worker(self):
self.btest('webaudio/audioworklet_worker.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'], expected='1')
self.btest_exit('webaudio/audioworklet_worker.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])

# Tests that posting functions between the main thread and the audioworklet thread works
@parameterized({
'': ([],),
'closure': (['--closure', '1', '-Oz'],),
})
# Note: doesn't need audio hardware (and has no AW code that tests 2GB or wasm64)
def test_audio_worklet_post_function(self, args):
self.btest('webaudio/audioworklet_post_function.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'] + args, expected='1')
self.btest_exit('webaudio/audioworklet_post_function.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'] + args)

@parameterized({
'': ([],),
'closure': (['--closure', '1', '-Oz'],),
})
@no_wasm64('https://github.com/emscripten-core/emscripten/pull/23508')
@no_2gb('https://github.com/emscripten-core/emscripten/pull/23508')
@requires_sound_hardware
def test_audio_worklet_modularize(self, args):
self.btest_exit('webaudio/audioworklet.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-sMODULARIZE=1', '-sEXPORT_NAME=MyModule', '--shell-file', test_file('shell_that_launches_modularize.html')] + args)
self.btest_exit('webaudio/audioworklet.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-sMODULARIZE=1', '-sEXPORT_NAME=MyModule', '--shell-file', test_file('shell_that_launches_modularize.html'), '-DTEST_AND_EXIT'] + args)

# Tests multiple inputs, forcing a larger stack (note: passing BROWSER_TEST is
# specific to this test to allow it to exit rather than play forever).
Expand Down
32 changes: 19 additions & 13 deletions test/webaudio/audioworklet.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include <emscripten/webaudio.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/* Steps to use Wasm-based AudioWorklets:
Expand All @@ -22,16 +20,17 @@
begin to fire.
*/

// REPORT_RESULT is defined when running in Emscripten test harness. You can
// strip these out in your own project.
#ifdef REPORT_RESULT
// TEST_AND_EXIT is defined when running in the Emscripten test harness. You can
// strip these out in your own project (otherwise playback will end quickly).
#ifdef TEST_AND_EXIT
_Thread_local int testTlsVariable = 1;
int lastTlsVariableValueInAudioThread = 1;
#endif

// This function will be called for every fixed-size buffer of audio samples to be processed.
bool ProcessAudio(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) {
#ifdef REPORT_RESULT
#ifdef TEST_AND_EXIT
// Only running in the test harness, see main_thread_tls_access()
assert(testTlsVariable == lastTlsVariableValueInAudioThread);
++testTlsVariable;
lastTlsVariableValueInAudioThread = testTlsVariable;
Expand Down Expand Up @@ -63,14 +62,15 @@ EM_JS(void, InitHtmlUi, (EMSCRIPTEN_WEBAUDIO_T audioContext), {
};
});

#ifdef REPORT_RESULT
#ifdef TEST_AND_EXIT
bool main_thread_tls_access(double time, void *userData) {
// Try to mess the TLS variable on the main thread, with the expectation that
// it should not change the TLS value on the AudioWorklet thread.
// it should not change the TLS value on the AudioWorklet thread, asserted in
// ProcessAudio().
testTlsVariable = (int)time;
// Exit to the test harness after enough calls to ProcessAudio()
if (lastTlsVariableValueInAudioThread >= 100) {
REPORT_RESULT(0);
return false;
emscripten_force_exit(0);
}
return true;
}
Expand All @@ -79,7 +79,7 @@ bool main_thread_tls_access(double time, void *userData) {
// This callback will fire after the Audio Worklet Processor has finished being
// added to the Worklet global scope.
void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {
if (!success) return;
assert(success && "Stopped in AudioWorkletProcessorCreated");

// Specify the input and output node configurations for the Wasm Audio
// Worklet. A simple setup with single mono output channel here, and no
Expand All @@ -97,7 +97,8 @@ void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool succe
// Connect the audio worklet node to the graph.
emscripten_audio_node_connect(wasmAudioWorklet, audioContext, 0, 0);

#ifdef REPORT_RESULT
#ifdef TEST_AND_EXIT
// Schedule this to exit after ProcessAudio() has been called 100 times
emscripten_set_timeout_loop(main_thread_tls_access, 10, 0);
#endif

Expand All @@ -108,7 +109,7 @@ void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool succe
// AudioWorklet global scope, and is now ready to begin adding Audio Worklet
// Processors.
void WebAudioWorkletThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {
if (!success) return;
assert(success && "Stopped in WebAudioWorkletThreadInitialized");

WebAudioWorkletProcessorCreateOptions opts = {
.name = "noise-generator",
Expand All @@ -132,4 +133,9 @@ int main() {
// and kick off Audio Worklet scope initialization, which shares the Wasm
// Module and Memory to the AudioWorklet scope and initializes its stack.
emscripten_start_wasm_audio_worklet_thread_async(context, wasmAudioWorkletStack, sizeof(wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, 0);

#ifdef TEST_AND_EXIT
// We're in the test harness and exiting is via main_thread_tls_access()
emscripten_exit_with_live_runtime();
#endif
}
15 changes: 8 additions & 7 deletions test/webaudio/audioworklet_post_function.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <emscripten/webaudio.h>
#include <stdio.h>
#include <assert.h>

// This test showcases posting messages (function calls) between the main thread
Expand All @@ -8,25 +7,25 @@

// This event will fire on the main thread.
void MessageReceivedOnMainThread(int d, int e, int f) {
printf("MessageReceivedOnMainThread: d=%d, e=%d, f=%d\n", d, e, f);
emscripten_outf("MessageReceivedOnMainThread: d=%d, e=%d, f=%d", d, e, f);
assert(!emscripten_current_thread_is_audio_worklet());
assert(d == 1 && e == 2 && f == 3);
#ifdef REPORT_RESULT
REPORT_RESULT(1); // test succeeded, were able to post a message from main thread to audio thread and back!
#endif

// test succeeded, were able to post a message from main thread to audio thread and back!
emscripten_force_exit(0);
}

// This event will fire on the audio worklet thread.
void MessageReceivedInAudioWorkletThread(int a, int b) {
printf("MessageReceivedInAudioWorkletThread: a=%d, b=%d\n", a, b);
emscripten_outf("MessageReceivedInAudioWorkletThread: a=%d, b=%d", a, b);
assert(emscripten_current_thread_is_audio_worklet());
assert(a == 42 && b == 9000);
emscripten_audio_worklet_post_function_viii(EMSCRIPTEN_AUDIO_MAIN_THREAD, MessageReceivedOnMainThread, /*d=*/1, /*e=*/2, /*f=*/3);
}

// This callback will fire when the audio worklet thread has been initialized.
void WebAudioWorkletThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {
printf("WebAudioWorkletThreadInitialized\n");
emscripten_out("WebAudioWorkletThreadInitialized");
emscripten_audio_worklet_post_function_vii(audioContext, MessageReceivedInAudioWorkletThread, /*a=*/42, /*b=*/9000);
}

Expand All @@ -39,4 +38,6 @@ int main() {
// and kick off Audio Worklet scope initialization, which shares the Wasm
// Module and Memory to the AudioWorklet scope and initializes its stack.
emscripten_start_wasm_audio_worklet_thread_async(context, wasmAudioWorkletStack, sizeof(wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, 0);

emscripten_exit_with_live_runtime();
}
16 changes: 9 additions & 7 deletions test/webaudio/audioworklet_worker.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include <emscripten/webaudio.h>
#include <emscripten/wasm_worker.h>
#include <emscripten/threading.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

// Tests that
Expand All @@ -13,17 +11,19 @@
int workletToWorkerFutexLocation = 0;
int workletToWorkerFlag = 0;

void do_exit() {
emscripten_terminate_all_wasm_workers();
emscripten_force_exit(0);
}

void run_in_worker() {
while (0 == emscripten_futex_wait(&workletToWorkerFutexLocation, 0, 30000)) {
if (workletToWorkerFlag == 1) {
printf("Test success\n");
emscripten_out("Test success");
emscripten_wasm_worker_post_function_v(EMSCRIPTEN_WASM_WORKER_ID_PARENT, &do_exit);
break;
}
}

#ifdef REPORT_RESULT
REPORT_RESULT(workletToWorkerFlag == 1);
#endif
}

// This event will fire on the audio worklet thread.
Expand All @@ -45,4 +45,6 @@ int main() {

EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(0);
emscripten_start_wasm_audio_worklet_thread_async(context, wasmAudioWorkletStack, sizeof(wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, 0);

emscripten_exit_with_live_runtime();
}
Loading