Skip to content
Open
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
7 changes: 7 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ See docs/process.md for more on how version tagging works.

4.0.22 (in development)
-----------------------
- It is now possible to load emscripten-generated code directly into an Audio
Worklet without using the `-sAUDIO_WORKLET` setting (which depends on shared
memory and `-sWASM_WORKERS`). To do this simply build with
`-sENVIRONMENT=worklet`. In this environment, because audio worklets don't
have a fetch API, you will need to either use `-sSINGLE_FILE` (to embed the
Wasm file), or use a custom `instantiateWasm` callback to supply the
Wasm module yourself. (#25942)
- Source maps now support 'names' field with function name information.
emsymbolizer will show function names when used with a source map. The size
of source maps may increase 2-3x and the link time can increase slightly due
Expand Down
8 changes: 8 additions & 0 deletions site/source/docs/api_reference/wasm_audio_worklets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Audio Worklets API is based on the Wasm Workers feature. It is possible to
also enable the `-pthread` option while targeting Audio Worklets, but the
audio worklets will always run in a Wasm Worker, and not in a Pthread.

.. note::
If you want to load an emscripten-generated program into an AudioContext that
you have created yourself, without depending on shared memory or
:ref:`WASM_WORKERS` you can add ``worklet`` to :ref:`ENVIRONMENT`. In this
mode, because Audio Worklets do not have any kind of fetch API, you will need
either use `-sSINGLE_FILE` (to embed the Wasm file), or use a custom
`instantiateWasm` to supply the Wasm module yourself (e.g. via `postMessage`).

Development Overview
====================

Expand Down
10 changes: 10 additions & 0 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -991,13 +991,18 @@ are:
- 'webview' - just like web, but in a webview like Cordova; considered to be
same as "web" in almost every place
- 'worker' - a web worker environment.
- 'worklet' - Audio Worklet environment.
- 'node' - Node.js.
- 'shell' - a JS shell like d8, js, or jsc.

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

Certain settings will automatically add to this list. For examble, building
with pthreads will automatically add `worker` and building with
``AUDIO_WORKLET`` will automatically add `worklet`.

Note that the set of environments recognized here is not identical to the
ones we identify at runtime using ``ENVIRONMENT_IS_*``. Specifically:

Expand Down Expand Up @@ -2502,6 +2507,11 @@ AUDIO_WORKLET
If true, enables targeting Wasm Web Audio AudioWorklets. Check out the
full documentation in site/source/docs/api_reference/wasm_audio_worklets.rst

Note: The setting will implicitly add ``worklet`` to the :ref:`ENVIRONMENT`,
(i.e. the resulting code and run in a worklet environment) but additionaly
depends on ``WASM_WORKERS`` and Wasm SharedArrayBuffer to run new Audio
Worklets.

Default value: 0

.. _audio_worklet_support_audio_params:
Expand Down
6 changes: 6 additions & 0 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@ function findWasmBinary() {
}
#endif

#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET && !AUDIO_WORKLET // AUDIO_WORKLET handled above
if (ENVIRONMENT_IS_AUDIO_WORKLET) {
return '{{{ WASM_BINARY_FILE }}}';
}
#endif

if (Module['locateFile']) {
return locateFile('{{{ WASM_BINARY_FILE }}}');
}
Expand Down
10 changes: 10 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -646,13 +646,18 @@ var LEGACY_VM_SUPPORT = false;
// - 'webview' - just like web, but in a webview like Cordova; considered to be
// same as "web" in almost every place
// - 'worker' - a web worker environment.
// - 'worklet' - Audio Worklet environment.
// - 'node' - Node.js.
// - 'shell' - a JS shell like d8, js, or jsc.
//
// This setting can be a comma-separated list of these environments, e.g.,
// "web,worker". If this is the empty string, then all environments are
// supported.
//
// Certain settings will automatically add to this list. For examble, building
// with pthreads will automatically add `worker` and building with
// ``AUDIO_WORKLET`` will automatically add `worklet`.
//
// Note that the set of environments recognized here is not identical to the
// ones we identify at runtime using ``ENVIRONMENT_IS_*``. Specifically:
//
Expand Down Expand Up @@ -1629,6 +1634,11 @@ var WASM_WORKERS = 0;

// If true, enables targeting Wasm Web Audio AudioWorklets. Check out the
// full documentation in site/source/docs/api_reference/wasm_audio_worklets.rst
//
// Note: The setting will implicitly add ``worklet`` to the :ref:`ENVIRONMENT`,
// (i.e. the resulting code and run in a worklet environment) but additionaly
// depends on ``WASM_WORKERS`` and Wasm SharedArrayBuffer to run new Audio
// Worklets.
// [link]
var AUDIO_WORKLET = 0;

Expand Down
1 change: 1 addition & 0 deletions src/settings_internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ var ENVIRONMENT_MAY_BE_WORKER = true;
var ENVIRONMENT_MAY_BE_NODE = true;
var ENVIRONMENT_MAY_BE_SHELL = true;
var ENVIRONMENT_MAY_BE_WEBVIEW = true;
var ENVIRONMENT_MAY_BE_AUDIO_WORKLET = true;

// Whether to minify import and export names in the minify_wasm_js stage.
// Currently always off for MEMORY64.
Expand Down
15 changes: 10 additions & 5 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var Module = moduleArg;
var Module;
// if (!Module)` is crucial for Closure Compiler here as it will otherwise replace every `Module` occurrence with a string
if (!Module) /** @suppress{checkTypes}*/Module = {"__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__":1};
#elif AUDIO_WORKLET
#elif ENVIRONMENT_MAY_BE_AUDIO_WORKLET
var Module = globalThis.Module || (typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {});
#else
var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {};
Expand All @@ -53,8 +53,11 @@ var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {
var ENVIRONMENT_IS_WASM_WORKER = globalThis.name == 'em-ww';
#endif

#if AUDIO_WORKLET
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
var ENVIRONMENT_IS_AUDIO_WORKLET = !!globalThis.AudioWorkletGlobalScope;
#endif

#if AUDIO_WORKLET
// Audio worklets behave as wasm workers.
if (ENVIRONMENT_IS_AUDIO_WORKLET) ENVIRONMENT_IS_WASM_WORKER = true;
#endif
Expand All @@ -79,7 +82,7 @@ var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;
// N.b. Electron.js environment is simultaneously a NODE-environment, but
// also a web environment.
var ENVIRONMENT_IS_NODE = {{{ nodeDetectionCode() }}};
#if AUDIO_WORKLET
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_AUDIO_WORKLET;
#else
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
Expand Down Expand Up @@ -352,7 +355,9 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
}
} else
#endif // ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER
#if AUDIO_WORKLET && ASSERTIONS
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
#endif
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET && ASSERTIONS
if (!ENVIRONMENT_IS_AUDIO_WORKLET)
#endif
{
Expand Down Expand Up @@ -401,7 +406,7 @@ if (ENVIRONMENT_IS_NODE) {
// if an assertion fails it cannot print the message
#if PTHREADS
assert(
#if AUDIO_WORKLET
#if ENVIRONMENT_MAY_BE_AUDIO_WORKLET
ENVIRONMENT_IS_AUDIO_WORKLET ||
#endif
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)');
Expand Down
38 changes: 38 additions & 0 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5478,6 +5478,44 @@ def test_audio_worklet_params_mixing(self, args):
def test_audio_worklet_emscripten_locks(self):
self.btest_exit('webaudio/audioworklet_emscripten_locks.c', cflags=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-pthread'])

def test_audio_worklet_direct(self):
self.add_browser_reporting()
self.emcc('hello_world.c', ['-o', 'hello_world.mjs', '-sEXPORT_ES6', '-sSINGLE_FILE', '-sENVIRONMENT=worklet'])
create_file('worklet.mjs', '''
import Module from "./hello_world.mjs"
console.log("in worklet");
class MyProcessor extends AudioWorkletProcessor {
constructor() {
super();
Module().then(() => {
console.log("done Module constructor");
this.port.postMessage("ready");
});
}
process(inputs, outputs, parameters) {
return true;
}
}
registerProcessor('my-processor', MyProcessor);
console.log("done register");
''')
create_file('test.html', '''
<script src="browser_reporting.js"></script>
<script>
async function createContext() {
const context = new window.AudioContext();
await context.audioWorklet.addModule('worklet.mjs');
const node = new AudioWorkletNode(context, 'my-processor');
node.port.onmessage = (event) => {
console.log(event);
reportResultToServer(event.data);
}
}
createContext();
</script>
''')
self.run_browser('test.html', '/report_result?ready')

# Verifies setting audio context sample rate, and that emscripten_audio_context_sample_rate() works.
@requires_sound_hardware
@also_with_minimal_runtime
Expand Down
10 changes: 7 additions & 3 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
'__main_argc_argv',
]

VALID_ENVIRONMENTS = ('web', 'webview', 'worker', 'node', 'shell')
VALID_ENVIRONMENTS = {'web', 'webview', 'worker', 'node', 'shell', 'worklet'}

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

Expand Down Expand Up @@ -184,6 +184,9 @@ def setup_environment_settings():
if settings.SHARED_MEMORY and settings.ENVIRONMENT:
settings.ENVIRONMENT.append('worker')

if settings.AUDIO_WORKLET:
settings.ENVIRONMENT.append('worklet')

# Environment setting based on user input
if any(x for x in settings.ENVIRONMENT if x not in VALID_ENVIRONMENTS):
exit_with_error(f'Invalid environment specified in "ENVIRONMENT": {settings.ENVIRONMENT}. Should be one of: {",".join(VALID_ENVIRONMENTS)}')
Expand All @@ -193,6 +196,7 @@ def setup_environment_settings():
settings.ENVIRONMENT_MAY_BE_NODE = not settings.ENVIRONMENT or 'node' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_SHELL = not settings.ENVIRONMENT or 'shell' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_WORKER = not settings.ENVIRONMENT or 'worker' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_AUDIO_WORKLET = not settings.ENVIRONMENT or 'worklet' in settings.ENVIRONMENT

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

Expand Down Expand Up @@ -2472,7 +2476,7 @@ def module_export_name_substitution():
logger.debug(f'Private module export name substitution with {settings.EXPORT_NAME}')
src = read_file(final_js)
final_js += '.module_export_name_substitution.js'
if settings.MINIMAL_RUNTIME and not settings.ENVIRONMENT_MAY_BE_NODE and not settings.ENVIRONMENT_MAY_BE_SHELL and not settings.AUDIO_WORKLET:
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:
# On the web, with MINIMAL_RUNTIME, the Module object is always provided
# via the shell html in order to provide the .asm.js/.wasm content.
replacement = settings.EXPORT_NAME
Expand Down