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

Creating ~50 nodes via .getNode() yields "RangeError: WebAssembly.Instance(): Out of memory: wasm memory" #20

Open
dxinteractive opened this issue Aug 22, 2022 · 2 comments

Comments

@dxinteractive
Copy link

dxinteractive commented Aug 22, 2022

I'm unsure if this is a usage problem by me, an underlying limitation or a bug (doubtful).

If I create ~50 AudioWorkletNodes via getNode(), I get the out of memory error mentioned in the title on latest Chrome on macOS.

image

Here is a reproduction based on test/mono.html:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script src="../dist/index.min.js"></script>
    </head>
    <body style="position: absolute; width: 100%; height: 100%; margin: 0px">
    </body>
    <script>
        audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        const faust = new Faust2WebAudio.Faust({ debug: true, wasmLocation: "../dist/libfaust-wasm.wasm", dataLocation: "../dist/libfaust-wasm.data" });
        window.faust = faust;
        faust.ready
        .then(async () => {
            const code = `
import("stdfaust.lib");
process = ba.pulsen(1, 10000) : pm.djembe(60, 0.3, 0.4, 1) <: dm.freeverb_demo;`;

            const node = await faust.getNode(code, { audioCtx, useWorklet: true, args: { "-I": "libraries/" } });
            node.connect(audioCtx.destination);
            window.node = node;

            let i = 1;
            const another = async () => {
                console.log('five seconds have elapsed, make another', i++);
                const node = await faust.getNode(code, { audioCtx, useWorklet: true, args: { "-I": "libraries/" } });
                await new Promise(r => setTimeout(r, 5000));
                node.destroy();
                another();
            };
            another();
        })
        const unlockAudioContext = (audioCtx) => {
            if (audioCtx.state !== "suspended") return;
            const b = document.body;
            const events = ["touchstart", "touchend", "mousedown", "keydown"];
            const unlock = () => audioCtx.resume().then(clean);
            const clean = () => events.forEach(e => b.removeEventListener(e, unlock));
            events.forEach(e => b.addEventListener(e, unlock, false));
        }
        unlockAudioContext(audioCtx);
    </script>
</html>

Normally I wouldn't want 50 nodes connected at the same time, but I would like to be able to compile 50 nodes over the course of half an hour without having to refresh the page. I'm calling node.destroy(); but perhaps this isn't enough to clean up the memory used by each new AudioWorklet. In this example I'm not connecting the new nodes to the audio graph so there should be no hanging references on the js side (honestly I'm not sure how WebAssembly memory allocation and js's regular garbage colllection interact, if at all). Is there anything I can do to overcome this limitation?

@dxinteractive
Copy link
Author

Further research makes it seems like this might be just how memory allocation behaves with WebAssembly. I don't know enough to say definitively though. I can make Faust IDE stop working with the same error by compiling a patch ~50 times, or compiling ~50 different patches.

@dxinteractive
Copy link
Author

dxinteractive commented Aug 23, 2022

Silly hack that circumvents the issue: put all the faust audio compilation stuff in a newly-created iframe, and remove the iframe and recreate the iframe after every compile. When the iframe is gone, it seems the resources attached to that iframe are gone too, or at the very least you get a new memory pool to use when the new iframe is loaded. I'm sure this is potentially unsuitable for many reasons, but I'm going to see how far I can take this idea 😂

How I got here: I'm making a web app that renders audio in an OfflineAudioContext, and most nodes are dynamically compiled Faust AudioWorkletNodes (although not using this library). I'm using a mix of mainly Faust, and a few other nodes to do things that Faust can't do like web audio's native convolution node, which is why I'm interested in OfflineAudioContexts in the first place. But after ~50 offline renders I have been hitting the same limitations as described above. It's much easier to hit this limit with offline rendering because OfflineAudioContexts can't be reused, and new audio nodes need to be created for every new render, even if they are functionally identical, which I find to be an awkward situation. And creating all those new nodes leads to the "out of memory" error I described above. I then searched to see if anyone else hit this issue, found this library, but then realised the issue still exists here too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant