[WIP] FaustGen - a UGen for interpreting Faust code

Thanks @Spacechild1, all that is well above my limited understanding of SC internals, so I’m completely opened to improvement you suggest.

Would you like a write on my GitHub - sletz/faustgen-supercollider: Livecode Faust in SuperCollider using an embedded Faust compiler. fork? This would probably be the simplest solution to move on ?

1 Like

Would you like a write on my GitHub - sletz/faustgen-supercollider: Livecode Faust in SuperCollider using an embedded Faust compiler. fork?

I think I can just make my own fork and then make a PR. Anyway, I’m busy now with other things until the end of April, but please ping me in case I forget!

(I really want to fix these unit command issues. There are many potential Server plugins that follow a similar pattern and we really need a standardized and workable solution. Nobody should go through the same pain as I had when developing VSTPlugin :face_with_spiral_eyes:)

1 Like

I’m not all that familiar with these parts of the code base so this might be unreasonable, but what do you think about making a new osc command that takes in the Faust code as a string, and turns it into a ugen.

This way users could write something like this…

FaustGen(\myEffect, ... What ever... ).add;

x = { FaustGen(\myEffect).ar( SinOsc.ar ) }.play;

y = { FaustGen(\myEffect).ar( LFTri.ar ) }.play;

Then update all instances of \myEffect at once (perhaps with some channel count checking)? This would be similar to one of the other Faust2Ugen projects, but let you define ugens while the server runs.

1 Like

Commit for RT allocations (will have to be tested) Define rtalloc_memory_manager to properly use RT allocation of DSP in… · sletz/faustgen-supercollider@bea605b · GitHub.

That’s an excellent idea! See also my comment on GitHub: Define rtalloc_memory_manager to properly use RT allocation of DSP in… · sletz/faustgen-supercollider@bea605b · GitHub

I would really prefer to have a FaustDef class that compiles Faust code and binds the resulting factory to a name. FaustGen would then simply reference a FaustDef by name. The actual Faust DSP instance could be created synchronously (i.e. without async commands) and we probably wouldn’t even need FaustGenController. Last but not least, updates to FaustDef could automatically update existing UGen instances!

1 Like

Might it be worth then working on extending the server to let you load any shared object as a ugen at runtime? This behaviour doesn’t need to be tied to Faust. This way compilation to the shared object could happen literally anywhere else than inside the sever.

FYI, @Spacechild1 your link to a comment is a repeat of @Stephane_Letz previous link.

FaustDef.add would just send a plugin command (/cmd) to the Server to compile Faust code and store the resulting factory in a dictionary under the given name. The FaustGen UGen would simply look up the factory in the dictionary and create a new instance.

I don’t see how one would generalize this…

FYI, @Spacechild1 your link to a comment is a repeat of @Stephane_Letz previous link.

Don’t you see the comment?

Well we already have a way of loading plugins from shared objects, it’s just this happens at startup. Would it be possible to extend this so you can load new plugins, and redefined existing plugins, while the server runs? Are these ugens already stored in a dictionary that is looked up by name? If so, surely we can just replace them (on a thread safe way).

This way, you could have the Faust compile run in sclang (or elsewhere) and you could even use other languages to make these shared objects, even normal C++ plugins could become hot swappable?

That’s not how the Server works. A Server plugin registers UGen definitions which consists of the struct size and ctor/dtor functions. The actual input and output structure is defined by the SynthDef. Therefore it’s important that the .scx object and the corresponding sclang class must be kept in sync, otherwises the UGen can crash or behave weirdly. In theory, one could redefine a UnitDef at runtime, but that would be pretty dangerous, for this very reason.

Also, the Faust compiler does not produce an actual shared object on the filesystem, it returns a (dynamically compiled) C++ factory object that can create the actual DSP objects. So in this case the compilation needs to happen on the Server.

Finally, if you want to register Faust objects as UGens, they would need a corresponding sclang class so that you can put them in a SynthDef. In fact, such a tool already exists with faust2sc (faust/tools/faust2appls/faust2sc.py at master-dev · grame-cncm/faust · GitHub), but it does not allow for dynamic code compilation (for the reasons mentioned above). That’s why the FaustGen project has been started in the first place.

Ah, I see, that does sound quite fragile!

Surely you could just pass an identity dictionary and overload all the methods? …but your previous point makes my suggestion moot anyway.

Thanks for explaining this :grinning: I learnt something!

1 Like

Stephane,

In trying to build this, I get the following error:

In file included from /Users/spluta1/Dev/faust/faustgen-supercollider/faust/compiler/libcode.cpp:113:
/Users/spluta1/Dev/faust/faustgen-supercollider/faust/compiler/generator/llvm/clang_code_container.hh:29:10: fatal error: 'c_code_container.hh' file not found
   29 | #include "c_code_container.hh"
      |          ^~~~~~~~~~~~~~~~~~~~~
1 error generated.

The file is there, so I’m not sure why I am getting this error. I am able to build the max faustgen without issue, so I have llvm installed correctly.

EDIT: I plopped in the latest version of faust and this problem went away. Still working out the kinks, but I think I can figure it out.

Sam

Ping, Ping, Ping, Ping !

2 Likes