grame-cncm / faustwasm

Faust for WebAudio written in TypeScript
Other
13 stars 7 forks source link

Get AudioWorklets to play nicely with minifiers #1

Closed ijc8 closed 1 year ago

ijc8 commented 1 year ago

This PR resolves issues discovered while working on grame-cncm/fausteditor#4 whereby generated AudioWorklets would not work when using this library in projects with minifiers (as in the default vite configuration, which uses esbuild for minification).

Explanation

First issue: dependencies field name mismatch

FaustDspGenerator sets up dependencies in the AudioWorklet source code to be passed to getFaustAudioWorkletProcessor() like so: https://github.com/grame-cncm/faustwasm/blob/f04942a334f13fabf56da854c083fe865db270ee/src/FaustDspGenerator.ts#L192-L197

However, after minification, this would generate code like

const dependencies = {
    W,
    R,
    zt
}

in which the field names are also inadvertently minified, causing a mismatch with the fields expected in getFaustAudioWorkletProcessor(): https://github.com/grame-cncm/faustwasm/blob/f04942a334f13fabf56da854c083fe865db270ee/src/FaustAudioWorkletProcessor.ts#L50-L53

and causing an error:

image

The first commit in this PR fixes this by making the field names explicit, so the code looks like

const dependencies = {
    FaustBaseWebAudioDsp: W,
    FaustMonoWebAudioDsp: R,
    FaustWasmInstantiator: zt
};

and matches the names expected in getFaustAudioWorkletProcessor.

Second issue: mismatch between definition and use due to aliased class

The second issue was a bit trickier. One of the classes injected in the AudioWorklet is FaustWasmInstantiator, which depends on FaustMonoDspInstance. FaustMonoDspInstance is also injected into the AudioWorklet, but the names weren't matching up.

Screenshot from 2023-07-03 13-18-34

The body of FaustWasmInstantiator referred to FaustMonoDspInstance as $...

Screenshot from 2023-07-03 13-21-46

...but it was injected with the name zt_default.

Screenshot from 2023-07-03 13-21-28

There were two issues here: using export default caused the class to be aliased by another name (so that FaustDspInstance.name did not match up with the name actually used in FaustWasmInstantiator).

Screenshot from 2023-07-03 13-24-58

And, presumably to work around the behavior of another minification or bundling process, _default was appended in the generated AudioWorklet source.

https://github.com/grame-cncm/faustwasm/blob/f04942a334f13fabf56da854c083fe865db270ee/src/FaustDspGenerator.ts#L188

The second commit resolves these by just exporting and importing FaustDspInstance directly, rather than using export default, and removing the previous _default workaround.

Remarks

This is a bit complex because of how separate AudioWorklets are from the rest of the code, and because faustwasm generates the code for them at runtime. In doing so, it relies on runtime introspection features like Function.name and Function.toString() to inject source code into AudioWorklets. These still work in the presence of tools that transform the source code at compile-time (minifiers, bundlers, transpilers...), but, much like eval(), they require care to ensure that the generated source will remain valid in the presence of such transformations - which generally preserve the correctness of code but can affect introspection features.

sletz commented 1 year ago

Seems OK for me. @Fr0stbyteR any comment here ?

Fr0stbyteR commented 1 year ago

Sounds good!

Fr0stbyteR commented 1 year ago

We may need to add test/faustlive-wasm/faustwasm and test/faustlive-wasm/faust-ui into gitignore. These are also generated files

Fr0stbyteR commented 1 year ago

2