diff --git a/make.lua b/make.lua index 84d97c8..880877c 100644 --- a/make.lua +++ b/make.lua @@ -109,6 +109,7 @@ lm:conf { defines = { "_POSIX_C_SOURCE=200809L", "_GNU_SOURCE", + "MA_ENABLE_AUDIO_WORKLETS", }, }, defines = { @@ -134,9 +135,10 @@ lm:exe "soluna" { deps = deps, emcc = { ldflags = { - "-s MAIN_MODULE=1", - "-Wl,-u,emscripten_builtin_memalign", - "-Wl,--export=emscripten_builtin_memalign", + "-sJSPI", + "-sAUDIO_WORKLET=1", + "-sWASM_WORKERS=1", + "--js-library=src/platform/wasm/soluna_audio_wasm.js", }, }, } diff --git a/src/platform/wasm/soluna_audio_wasm.js b/src/platform/wasm/soluna_audio_wasm.js new file mode 100644 index 0000000..6721ffa --- /dev/null +++ b/src/platform/wasm/soluna_audio_wasm.js @@ -0,0 +1,37 @@ +// Override emscripten_request_animation_frame_loop to support JSPI. +// +// When JSPI is enabled (-sJSPI), audio worklet initialization calls +// emscripten_sleep() which requires the WebAssembly call stack to be +// in a "promising" context (wrapped with WebAssembly.promising). +// +// This override wraps the C frame callback with WebAssembly.promising +// so that emscripten_sleep works correctly during audio init. +mergeInto(LibraryManager.library, { + emscripten_request_animation_frame_loop__deps: ['$getWasmTableEntry'], + emscripten_request_animation_frame_loop__sig: 'vpp', + emscripten_request_animation_frame_loop: function(cb, userData) { + var fn = getWasmTableEntry(cb); + var promisingFn = null; + if (typeof WebAssembly !== 'undefined' && typeof WebAssembly.promising === 'function') { + try { + promisingFn = WebAssembly.promising(fn); + } catch (e) { + promisingFn = null; + } + } + function tick(rAF_time) { + if (promisingFn) { + promisingFn(rAF_time, userData).then(function(keepGoing) { + if (keepGoing) requestAnimationFrame(tick); + }).catch(function(e) { + console.error('Soluna: JSPI frame callback error (WebAssembly.promising):', e instanceof Error ? e.message : String(e), e); + }); + } else { + if (fn(rAF_time, userData)) { + requestAnimationFrame(tick); + } + } + } + requestAnimationFrame(tick); + }, +});