Repository navigation
Any plans to support WASM / Browser? #9
Description
Activity
In theory, support should be available. The reason it wasn't supported at launch is due to a misunderstanding on my part, as I initially misread "Emscripten" as "ECMAScript" when I saw it alongside HTML5, likely due to my declining eyesight. I'll add it to my TODO list, and it should be implemented in the next few commits.
Reacted by Andreas, ECH, Stephen Monaco, Timothée Lecomte, Nick Kovalsky and orrestI'll add it to my TODO list, and it should be implemented in the next few commits.
Cool, thank you. Let me know if you need help or testing.
BTW: I found this article, I don't know if this is helpful, if not, just ignore it :-)
In theory, support should be available. The reason it wasn't supported at launch is due to a misunderstanding on my part, as I initially misread "Emscripten" as "ECMAScript" when I saw it alongside HTML5, likely due to my declining eyesight. I'll add it to my TODO list, and it should be implemented in the next few commits.
I would love if you added WASM support!
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed if no further activity occurs.If this issue is still relevant, please leave a comment indicating that you would like it to remain open.
Thank you for your contributions.+1
This should not be auto-stale :-)I'm also interested in using this in the FlatRedBall engine that I maintain. I have been very frustrated with NAudio's lack of updates and similar lack of platforms. If I could get this to work on WASM then this could be the audio solution we use at FRB.
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed if no further activity occurs.If this issue is still relevant, please leave a comment indicating that you would like it to remain open.
Thank you for your contributions.This shouldn't be marked as stale.
The WASM support was actually the most problematic issue I worked on, mainly because the JSInterop lacks complex marshalling. And while I have finally tackled that and got compilation done, for some reason, the
JSImportattribute andJSHostcan't seem to find the exported functions even though I can call them from the browser console.I will get busy for the next two weeks, so this will be delayed, sorry.
I will get busy for the next two weeks, so this will be delayed, sorry.
If you need help with the
JSHost/JSImportstuff, feel free to reach out. While I'm not an expert, I would recommend to (if possible) NOT useJSHostat all and useglobalThisinstead.globalThisrefers towindowand you can translate objects and functions without using any ofJSHost- so[JSImport("globalThis.localStorage.getItem")]refers towindow.localStorage.getItem, which basically means that you can map global functions withoutJSHost.See https://github.com/sandreas/Avalonia.SimplePreferences/blob/335879bb0554a444ef218da53d03f1d17e212829/Avalonia.SimplePreferences/Storages/BrowserStorage.cs#L61 for an example.
However, since
miniaudioWASM probably will spit out ajsfile which needs to be loaded, you could refer to a more elaborate explanation how to useJSHost, which is burried in the AvaloniaUI discussions containing my notes on how to do it:
AvaloniaUI/Avalonia#18139 (reply in thread)Hope it helps. Good luck!
Any help would be appreciated. I uploaded my last changes regarding the WASM support to branch wasm-platform. Currently, it loads, but on calling methods like
AllocateContextfrom C# code, which returns a pointernint/IntPtr, I got this error in the browser console.MONO_WASM: Exception marshalling result of JS promise to CS: RuntimeError: memory access out of bounds at mono_class_get_image (dotnet.native.wasm:0x68dd07) at get_native_to_interp (dotnet.native.wasm:0x8d7ea4) at mono_wasm_get_native_to_interp_trampoline (dotnet.native.wasm:0x754c60) at interp_create_method_pointer (dotnet.native.wasm:0x62d363) at jit_compile_method_with_opt_cb (dotnet.native.wasm:0x73f143) at mono_llvm_cpp_catch_exception (dotnet.native.wasm:0x65175e) at jit_compile_method_with_opt (dotnet.native.wasm:0x73f053) at mono_jit_compile_method (dotnet.native.wasm:0x73ee76) at mono_compile_method_checked (dotnet.native.wasm:0x7056e7) at mono_delegate_to_ftnptr_impl (dotnet.native.wasm:0x6e8f0f) pt @ logging.ts:26 (anonymous) @ marshal-to-cs.ts:339 Promise.then mo @ marshal-to-cs.ts:329 (anonymous) @ marshal-to-cs.ts:83 (anonymous) @ invoke-js.ts:202 Ll @ invoke-js.ts:276 $do_icall @ dotnet.native.wasm:0x631112 $do_icall_wrapper @ dotnet.native.wasm:0x62c057 $mono_interp_exec_method @ dotnet.native.wasm:0x61df69 $interp_runtime_invoke @ dotnet.native.wasm:0x62d96c $mono_jit_runtime_invoke @ dotnet.native.wasm:0x742c70 $do_runtime_invoke @ dotnet.native.wasm:0x7034d0 $mono_runtime_invoke_checked @ dotnet.native.wasm:0x70346a $mono_runtime_try_invoke_byrefs @ dotnet.native.wasm:0x709b83 $ves_icall_InternalInvoke @ dotnet.native.wasm:0x6cee04 $ves_icall_InternalInvoke_raw @ dotnet.native.wasm:0x6d7c7b $do_icall @ dotnet.native.wasm:0x6311cb $do_icall_wrapper @ dotnet.native.wasm:0x62c057 $mono_interp_exec_method @ dotnet.native.wasm:0x61df69 $interp_runtime_invoke @ dotnet.native.wasm:0x62d96c $mono_jit_runtime_invoke @ dotnet.native.wasm:0x742c70 $do_runtime_invoke @ dotnet.native.wasm:0x7034d0 $mono_runtime_try_invoke @ dotnet.native.wasm:0x703f50 $mono_runtime_invoke @ dotnet.native.wasm:0x706acd $mono_wasm_invoke_method_bound @ dotnet.native.wasm:0x8d87bf (anonymous) @ dotnet.native.js:936 ccall @ dotnet.native.js:8977 (anonymous) @ dotnet.native.js:8996 kr @ invoke-cs.ts:273 l.javaScriptExports.call_entry_point @ managed-exports.ts:60 Fc @ run.ts:44 (anonymous) @ main.js:13while calling the method
window.miniaudioModule._sf_allocate_contextdirectly from browser console just works fine and return this (I assume this is what a pointer seems in javascript),(...args) => { assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); var f = wasmExports[name]; assert(f, `exported native function \`${name}\…Currently, it loads, but on calling methods like AllocateContext from C# code, which returns a pointer nint/IntPtr, I got this error in the browser console.
While this may be a problem in your code, it might also be a problem in C# WASM for specific systems (see dotnet/runtime#105893). However, it may take some time until I can take a look - pretty busy right now. Let's hope for the best.
Here's another interesting link I found: https://stackoverflow.com/questions/57938081/blazor-preview-9-mono-wasm-memory-access-out-of-bounds-max-string-size-for-dotn
Did you already consult an LLM?
16 remaining items
Can this action be configured to check for a specific tag (e.g., 'persisted-issue') before marking an item as stale?
Can this action be configured to check for a specific tag (e.g., 'persisted-issue') before marking an item as stale?
with: exempt-issue-labels: 'work-in-progress'Then label the issue with
work-in-progress. See https://github.com/actions/stale?tab=readme-ov-file#exempt-issue-labelsReacted by Ahmed Abdallah and Nathanial P. Howard- addedin-progressThis issue is actively being worked on; development is currently in progress.This issue is actively being worked on; development is currently in progress.
on Jun 2, 2025 I would also like this see work!
Currently, I'm wrapping NAudio with SoundFlow on desktop, and for the browser version I'm using an AudioWorkletNode (web standard) that reads from an NAudio provider.I would also like this see work!
The problem is: It isn't that easy. I tried (not too hard I admit) but somehow it did not work. I moved on and focused on a native Android App for my project instead of trying to bend C# to something it was not meant for. WASM would have been nice, but I did just not want even more hoops to jump through. Maybe some day I find the time to try again, because I really like this library.
It has been a long time since the last update. I hadn't made much progress because I thought the issue required JS knowledge (which is something I usually avoid—sorry!). However, since I shipped the latest update yesterday, I didn't want to leave without trying.
I found that JavaScript marshalling was still limiting us; callbacks refuse to marshal correctly no matter how much I dig in. I won't go into detail as my time is limited, but I discovered that we can actually use a Native approach without interacting with JavaScript at all. If I had realized this earlier, we would have had WASM support a year ago (sorry again)!
I spent all day trying the JS Interop approach, but a few hours ago I switched to the Native approach. Even though there aren't many resources for it, I’ve made much better progress. It’s not finished yet, and I’m afraid I haven't slept in over 40 hours. I need to get a few hours of sleep now because I have to handle a week's worth of preparations as soon as I wake up. (I think I can put 'works well under pressure' on my resume now, haha). Enough chit-chat, here's the current state
The Setup
Everything is pushed to the platform/wasm-browser branch, based on the current master branch (v1.4.0), the old wasm-platform still there but it's outdated.
-
Native Build:
TheNative\miniaudio-backend\CMakeLists.txtis updated to detect Emscripten. It builds a static library (libminiaudio.a) instead of a shared object, which is required for .NET WASM module to link against. It uses the flags-msimd128and-fwasm-exceptionsto match the .NET 8 runtime. -
Managed Build:
I addedSoundFlow.Samples.AvaloniaCrossPlatformto test this. The mainSoundFlowproject is multi-targeted to includenet8.0-browser.
How to Replicate (The Development Loop)
To get to the state where you can see the error, you need to do the following:
- Run the standard build script:
Native\miniaudio-backend\build-wasm\build.ps1- This file is configured to use Emscripten bundled with dotnet, modify paths based on your setup.
- This successfully compiles the C code into
libminiaudio.a.
- The Manual Step:
- Usually I would place the
libminiaudio.afile inSrc\Backends\MiniAudio\runtimes\browser-wasm\native\, but during my debugging loop, I placed it inSamples\SoundFlow.Samples.AvaloniaCrossPlatform\SoundFlow.Samples.AvaloniaCrossPlatform.Browserand configured the browser sample to link it from there to avoid potential paths issues. - So, to ensure the build picks it up, I was copying
libminiaudio.adirectly to the root of theSoundFlow.Samples.AvaloniaCrossPlatform.Browserproject folder (I already left a copy there).
- Usually I would place the
- Run the
SoundFlow.Samples.AvaloniaCrossPlatform.Browserproject.
The Blockade (Current Crash)
The project builds and launches, but crashes immediately when the audio engine initializes and tries to call into C code.
Incase you removed the
if (!OperatingSystem.IsBrowser())check atstatic Native()inSrc/Backends/MiniAudio/Native.cs, it will loadNativeLibrary.GetMainProgramHandle()and you should get this log in browser consoleThe Error:
logging.ts:26 MONO_WASM: Exception marshalling result of JS promise to CS: ExitStatusmessage: "Program terminated with exit(1)"name: "ExitStatus"silent: truestack: "Error\n at Object.Me [as mono_exit] (https://localhost:5001/_framework/dotnet.js:3:18993)\n at Fl.e.onAbort.e.onAbort (https://localhost:5001/_framework/dotnet.runtime.js:3:215571)\n at abort (https://localhost:5001/_framework/dotnet.native.js:859:22)\n at _dlopen (https://localhost:5001/_framework/dotnet.native.js:5296:7)\n at SystemNative_GetDefaultSearchOrderPseudoHandle (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[27957]:0x8949aa)\n at do_icall (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[16249]:0x637f86)\n at do_icall_wrapper (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[16134]:0x632f0a)\n at mono_interp_exec_method (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[16127]:0x624cf7)\n at interp_runtime_invoke (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[16167]:0x63481f)\n at mono_jit_runtime_invoke (https://localhost:5001/_framework/dotnet.native.wasm:wasm-function[20704]:0x749bee)"status: 1[[Prototype]]: Object pt @ logging.ts:26 (anonymous) @ marshal-to-cs.ts:339What I have tried (See
Src/Backends/MiniAudio/Native.cs):After I added
if (!OperatingSystem.IsBrowser())tostatic Native()to prevent resolver from running in browser environment, I have tried every standard variation for theLibraryImportlibrary name string for static linking in Blazor/WASM:[LibraryImport("__Internal")](The standard way)[LibraryImport("miniaudio")][LibraryImport("libminiaudio")][LibraryImport("*")]
You will see my comment in
Native.cs:#if BROWSER private const string LibraryName = "__Internal"; // Nothing works, not "__Internal" or "*" or "miniaudio" #else private const string LibraryName = "miniaudio"; #endif
None of them work. Throws
DllNotFoundException:dotnet.native.js:1640 System.DllNotFoundException: miniaudio dotnet.native.js:1640 at SoundFlow.Backends.MiniAudio.MiniAudioEngine.InitializeBackend() in PATH\TO\SoundFlow\Src\Backends\MiniAudio\MiniAudioEngine.cs:line 150 dotnet.native.js:1640 at SoundFlow.Backends.MiniAudio.MiniAudioEngine..ctor(IEnumerable`1 backendPriority) in PATH\TO\SoundFlow\Src\Backends\MiniAudio\MiniAudioEngine.cs:line 113 dotnet.native.js:1640 at SoundFlow.Samples.AvaloniaCrossPlatform.ViewModels.MainViewModel.InitializeAudioEngine() in PATH\TO\SoundFlow\Samples\SoundFlow.Samples.AvaloniaCrossPlatform\SoundFlow.Samples.AvaloniaCrossPlatform\ViewModels\MainViewModel.cs:line 51At this point we need Microsoft to tell us, how to call P/Invokes that got linked from
dotnet.native.wasmReacted by Andreas and AxwaboReacted by Axwabo-
First I wanna thank you. Not only for making this project happen, but also for putting that much effort in this issue. I hope that you can see it like I always do, when something takes much more effort than I thought it would:
I learned a lot on the way, so the time was not wastedI have indeed learned a lot reading your thoughts on this issue. Maybe I throw this into an LLM and see what gets recommended (which you might already have tried).
Unfortunately I recently made progress in a private project I dreamed about for years and which is kind of my endboss: A tiny DIY portable embedded audio player with touch screen controls (inspired by and to replace my iPod Nano 7g for audio book listening one day). This thing is eating up every single bit of free time ATM (God I currently hate Rust and its learning curve). However, I might take a look at this as soon as I get stuck with the player.
I wish you all the best for your service and I really hope you return healthy ASAP.
sandreas
Reacted by Ahmed AbdallahBump, any way I can help?
I have started work on fixing this on my own fork.
I have fixed the imports simply by wrapping the native imports in pre-processor directives to check from browser and using
DllImportinstead ofLibraryImportas shown in the example below.#if BROWSER [DllImport("libminiaudio", EntryPoint = "ma_encoder_uninit")] public static extern void EncoderUninit(nint pEncoder); #else [LibraryImport(LibraryName, EntryPoint = "ma_encoder_uninit")] public static partial void EncoderUninit(nint pEncoder); #endif
However, now the compiler is complaining about the use of
UnmanagedFunctionPointerforOnAudioDatainMiniAudioEnginenot actually using a function pointer.I will update this issue with any further progress.
Reacted by Andreas and Nathanial P. HowardDespite fixing the interop, I now have several todo items to fully implement WASM support.
See #109
Happy to see some progress here. Honestly, after being offline for over 10 years, anything web- or browser-related still throws me off a bit, so I haven’t really touched WebAssembly until this.
I see a lot of refactoring in this PR. Is updating to .NET 10 strictly necessary? If so, I think it should wait for v2.0. I’m planning to release v2.0 once my current situation settles down.
I have some points on the PR, will add them to a separate comment there.
Reacted by Kieran FootThe update to .NET 10 was mainly for the performance gains in the updated runtime as a whole, so no, not required at all, but a nice addition.
The update to .NET 10 was mainly for the performance gains in the updated runtime as a whole, so no, not required at all, but a nice addition.
Bootsharp.com
Maybe it'll come in handy
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsIn progress
Hello,
first of all: Thanks for this great library.
Today I tried to use it with Linux and it worked flawlessly. Unfortunately when using an AvaloniaUI Cross Platform GUI Application and trying to start the WASM / Browser build, there is an error:
Since this is a wrapper for
miniaudioandminiaudioseems to supportWASMbuilds, I would like to ask, if you plan to integrate support forWASMor if I missed somethingExamples:
I basically ran all this using the AvaloniaUI xplat example and the SoundFlow example from the readme using a
MemoryStreamto rule out local file accessing errors: