Skip to content

Commit 39ec937

Browse files
committed
Add support for running output directly in Audio Worklet
There are some use cases where its useful to be able to run emscripten generated code in an audio worklet without building with `-sAUDIO_WORKLET`. One major one is for deploying in a context where shared memory not available (i.e. no cross origin isolation) Fixes: #25943
1 parent 22719c1 commit 39ec937

File tree

8 files changed

+64
-14
lines changed

8 files changed

+64
-14
lines changed

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,13 +991,18 @@ are:
991991
- 'webview' - just like web, but in a webview like Cordova; considered to be
992992
same as "web" in almost every place
993993
- 'worker' - a web worker environment.
994+
- 'worklet' - Audio Worklet environment.
994995
- 'node' - Node.js.
995996
- 'shell' - a JS shell like d8, js, or jsc.
996997

997998
This setting can be a comma-separated list of these environments, e.g.,
998999
"web,worker". If this is the empty string, then all environments are
9991000
supported.
10001001

1002+
Certain settings with automatically add this to this. For exmaple, building
1003+
with pthreads will automatically add `worker` and building with
1004+
``AUDIO_WORKLET`` will automatically add `worklet`.
1005+
10011006
Note that the set of environments recognized here is not identical to the
10021007
ones we identify at runtime using ``ENVIRONMENT_IS_*``. Specifically:
10031008

src/preamble.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,12 @@ function findWasmBinary() {
443443
}
444444
#endif
445445

446+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET && !AUDIO_WORKLET // AUDIO_WORKLET handled above
447+
if (ENVIRONMENT_IS_AUDIO_WORKLET) {
448+
return '{{{ WASM_BINARY_FILE }}}';
449+
}
450+
#endif
451+
446452
if (Module['locateFile']) {
447453
return locateFile('{{{ WASM_BINARY_FILE }}}');
448454
}

src/settings.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,13 +646,18 @@ var LEGACY_VM_SUPPORT = false;
646646
// - 'webview' - just like web, but in a webview like Cordova; considered to be
647647
// same as "web" in almost every place
648648
// - 'worker' - a web worker environment.
649+
// - 'worklet' - Audio Worklet environment.
649650
// - 'node' - Node.js.
650651
// - 'shell' - a JS shell like d8, js, or jsc.
651652
//
652653
// This setting can be a comma-separated list of these environments, e.g.,
653654
// "web,worker". If this is the empty string, then all environments are
654655
// supported.
655656
//
657+
// Certain settings with automatically add this to this. For exmaple, building
658+
// with pthreads will automatically add `worker` and building with
659+
// ``AUDIO_WORKLET`` will automatically add `worklet`.
660+
//
656661
// Note that the set of environments recognized here is not identical to the
657662
// ones we identify at runtime using ``ENVIRONMENT_IS_*``. Specifically:
658663
//

src/settings_internal.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,12 @@ var SOURCE_MAP_BASE = '';
135135
var SUPPORT_BASE64_EMBEDDING = false;
136136

137137
// the possible environments the code may run in.
138-
var ENVIRONMENT_MAY_BE_WEB = true;
139-
var ENVIRONMENT_MAY_BE_WORKER = true;
140-
var ENVIRONMENT_MAY_BE_NODE = true;
141-
var ENVIRONMENT_MAY_BE_SHELL = true;
142-
var ENVIRONMENT_MAY_BE_WEBVIEW = true;
138+
var ENVIRONMENT_MAY_BE_WEB = false;
139+
var ENVIRONMENT_MAY_BE_WORKER = false;
140+
var ENVIRONMENT_MAY_BE_NODE = false;
141+
var ENVIRONMENT_MAY_BE_SHELL = false;
142+
var ENVIRONMENT_MAY_BE_WEBVIEW = false;
143+
var ENVIRONMENT_MAY_BE_AUDIO_WORKLET = false;
143144

144145
// Whether to minify import and export names in the minify_wasm_js stage.
145146
// Currently always off for MEMORY64.

src/shell.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var Module = moduleArg;
3434
var Module;
3535
// if (!Module)` is crucial for Closure Compiler here as it will otherwise replace every `Module` occurrence with a string
3636
if (!Module) /** @suppress{checkTypes}*/Module = {"__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__":1};
37-
#elif AUDIO_WORKLET
37+
#elif ENVIRONMENT_MAY_BE_AUDIO_WORKLET
3838
var Module = globalThis.Module || (typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {});
3939
#else
4040
var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {};
@@ -53,8 +53,11 @@ var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {
5353
var ENVIRONMENT_IS_WASM_WORKER = globalThis.name == 'em-ww';
5454
#endif
5555

56-
#if AUDIO_WORKLET
56+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
5757
var ENVIRONMENT_IS_AUDIO_WORKLET = !!globalThis.AudioWorkletGlobalScope;
58+
#endif
59+
60+
#if AUDIO_WORKLET
5861
// Audio worklets behave as wasm workers.
5962
if (ENVIRONMENT_IS_AUDIO_WORKLET) ENVIRONMENT_IS_WASM_WORKER = true;
6063
#endif
@@ -79,7 +82,7 @@ var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;
7982
// N.b. Electron.js environment is simultaneously a NODE-environment, but
8083
// also a web environment.
8184
var ENVIRONMENT_IS_NODE = {{{ nodeDetectionCode() }}};
82-
#if AUDIO_WORKLET
85+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
8386
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_AUDIO_WORKLET;
8487
#else
8588
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
@@ -352,12 +355,14 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
352355
}
353356
} else
354357
#endif // ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER
355-
#if AUDIO_WORKLET && ASSERTIONS
358+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
359+
#endif
360+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET && ASSERTIONS
356361
if (!ENVIRONMENT_IS_AUDIO_WORKLET)
357362
#endif
358363
{
359364
#if ASSERTIONS
360-
throw new Error('environment detection error');
365+
throw new Error('XXXXXenvironment detection error');
361366
#endif // ASSERTIONS
362367
}
363368

@@ -401,7 +406,7 @@ if (ENVIRONMENT_IS_NODE) {
401406
// if an assertion fails it cannot print the message
402407
#if PTHREADS
403408
assert(
404-
#if AUDIO_WORKLET
409+
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
405410
ENVIRONMENT_IS_AUDIO_WORKLET ||
406411
#endif
407412
ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_NODE, 'Pthreads do not work in this environment yet (need Web Workers, or an alternative to them)');

test/test_browser.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5478,6 +5478,28 @@ def test_audio_worklet_params_mixing(self, args):
54785478
def test_audio_worklet_emscripten_locks(self):
54795479
self.btest_exit('webaudio/audioworklet_emscripten_locks.c', cflags=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-pthread'])
54805480

5481+
def test_audio_worklet_direct(self):
5482+
self.add_browser_reporting()
5483+
self.emcc('hello_world.c', ['-o', 'hello_world.mjs', '-sEXPORT_ES6', '-sSINGLE_FILE', '-sENVIRONMENT=worklet'])
5484+
create_file('worklet.mjs', '''
5485+
import Module from "./hello_world.mjs"
5486+
console.log("in worklet");
5487+
const m = await Module();
5488+
console.log("done");
5489+
''')
5490+
create_file('test.html', '''
5491+
<script src="browser_reporting.js"></script>
5492+
<script>
5493+
async function createContext() {
5494+
const audioContext = new window.AudioContext();
5495+
await audioContext.audioWorklet.addModule('worklet.mjs');
5496+
reportResultToServer("done");
5497+
}
5498+
createContext();
5499+
</script>
5500+
''')
5501+
self.run_browser('test.html', '/report_result?done')
5502+
54815503
# Verifies setting audio context sample rate, and that emscripten_audio_context_sample_rate() works.
54825504
@requires_sound_hardware
54835505
@also_with_minimal_runtime

tools/emscripten.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ def generate_js_compiler_input_hash(symbols_only=False):
194194
if not symbols_only:
195195
files += settings.PRE_JS_FILES
196196
files += settings.POST_JS_FILES
197+
# Also include other .js files that could be included in the output.
198+
files += glob.glob(utils.path_from_root('src/*.js'))
197199

198200
for file in sorted(files):
199201
file_contents.append(utils.read_file(file))

tools/link.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
'__main_argc_argv',
6868
]
6969

70-
VALID_ENVIRONMENTS = ('web', 'webview', 'worker', 'node', 'shell')
70+
VALID_ENVIRONMENTS = {'web', 'webview', 'worker', 'node', 'shell', 'worklet'}
7171

7272
EXECUTABLE_EXTENSIONS = ['.wasm', '.html', '.js', '.mjs', '.out', '']
7373

@@ -184,6 +184,9 @@ def setup_environment_settings():
184184
if settings.SHARED_MEMORY and settings.ENVIRONMENT:
185185
settings.ENVIRONMENT.append('worker')
186186

187+
if settings.AUDIO_WORKLET:
188+
settings.ENVIRONMENT.append('worklet')
189+
187190
# Environment setting based on user input
188191
if any(x for x in settings.ENVIRONMENT if x not in VALID_ENVIRONMENTS):
189192
exit_with_error(f'Invalid environment specified in "ENVIRONMENT": {settings.ENVIRONMENT}. Should be one of: {",".join(VALID_ENVIRONMENTS)}')
@@ -193,6 +196,7 @@ def setup_environment_settings():
193196
settings.ENVIRONMENT_MAY_BE_NODE = not settings.ENVIRONMENT or 'node' in settings.ENVIRONMENT
194197
settings.ENVIRONMENT_MAY_BE_SHELL = not settings.ENVIRONMENT or 'shell' in settings.ENVIRONMENT
195198
settings.ENVIRONMENT_MAY_BE_WORKER = not settings.ENVIRONMENT or 'worker' in settings.ENVIRONMENT
199+
settings.ENVIRONMENT_MAY_BE_AUDIO_WORKLET = not settings.ENVIRONMENT or 'worklet' in settings.ENVIRONMENT
196200

197201
if not settings.ENVIRONMENT_MAY_BE_NODE:
198202
if 'MIN_NODE_VERSION' in user_settings and settings.MIN_NODE_VERSION != feature_matrix.UNSUPPORTED:
@@ -1175,7 +1179,7 @@ def limit_incoming_module_api():
11751179
# In Audio Worklets TextDecoder API is intentionally not exposed
11761180
# (https://github.com/WebAudio/web-audio-api/issues/2499) so we also need to
11771181
# keep the JavaScript-based fallback.
1178-
if settings.SHRINK_LEVEL >= 2 and not settings.AUDIO_WORKLET and \
1182+
if settings.SHRINK_LEVEL >= 2 and not settings.ENVIRONMENT_MAY_BE_AUDIO_WORKLET and \
11791183
not settings.ENVIRONMENT_MAY_BE_SHELL:
11801184
default_setting('TEXTDECODER', 2)
11811185

@@ -2472,7 +2476,7 @@ def module_export_name_substitution():
24722476
logger.debug(f'Private module export name substitution with {settings.EXPORT_NAME}')
24732477
src = read_file(final_js)
24742478
final_js += '.module_export_name_substitution.js'
2475-
if settings.MINIMAL_RUNTIME and not settings.ENVIRONMENT_MAY_BE_NODE and not settings.ENVIRONMENT_MAY_BE_SHELL and not settings.AUDIO_WORKLET:
2479+
if settings.MINIMAL_RUNTIME and not settings.ENVIRONMENT_MAY_BE_NODE and not settings.ENVIRONMENT_MAY_BE_SHELL and not settings.ENVIRONMENT_MAY_BE_AUDIO_WORKLET:
24762480
# On the web, with MINIMAL_RUNTIME, the Module object is always provided
24772481
# via the shell html in order to provide the .asm.js/.wasm content.
24782482
replacement = settings.EXPORT_NAME

0 commit comments

Comments
 (0)