VCV Prototype

I don’t think a fixed panel with 8 knobs etc. would be a good idea. I would like to have at least the ability to load custom SVG files and instantiate my own user elements like knobs, sliders etc.

Looks like OSC with Faust has some kind of GUI elements. Instead of using OSC, this could be mapped to the VCV Rack controls. E.g. this Faust mixer (works only in Chrome) :

https://faust.grame.fr/tools/editor/?code=https://faust.grame.fr/doc/manual/img/src/exfaust136/exfaust136.dsp

would create and layout the GUI elements automatically, using VCV Rack controls like BefacoSlidePot. Bonus if it integrates a GUI editor to manually layout to fit a SVG design.

But the declarative style of Faust might be difficult for casual users that are not programmers. In my experience Lua is easy to use for programmer newbies. Once I used it to write games on the Playstation Portable, and it was quite popular, many users tried to write their first games with it:

http://www.luaplayer.org/gallery/index.html

A general script interface to the OpenGL canvas would be useful as well. Then someone could implement even things like a scope with it. But would be nice to provide some high-level API on top of it, but this can be implemented in the scripting languages itself, so that you can just call drawLine etc., as I did for my Lua Player:

http://web.archive.org/web/20060207034125/http://wiki.ps2dev.org/psp:lua_player:tutorial

Instead of Image.load, there could be some init-function which loads a SVG file and is called from the constructor of the module. But loading PNG or JPG files might be interesting, too, for some fast prototyping. And then forward all events like onAdd and onReset to the scripting environment.

I think the goal should be to allow to implement any module with a script, which is possible with C++. It would be just a little bit slower, and it would have no segmentation faults :slightly_smiling_face: and you wouldn’t need to design a panel for it.

1 Like

Specifying knobs/ports/buttons/lights dynamically is beyond what most prototypers want to do. I also want to discourage people from “publishing” their prototypes as finalized works. The idea is to be able to share patches which include their code so others can try, but offer a gateway drug for creating actual plugins. That’s why it will be called VCV Prototype. If many people started publishing “polished” modules with scripting languages, their modules, and therefore people’s average experience with Rack would:

  • be 2-200x slower than C++, depending on the scripting language
  • be 20-60MB per published module. With one VCV Prototype plugin, the weight of all scripting languages + LLVM is a one-time cost.
  • The API they used would not have as much support as the full C++ API.
  • They would not get access to custom C++ widgets, which are difficult enough in C++ to keep stable yet functional.

OpenGL is also way beyond what prototypers want to do. Unless you design a separate ShaderToy-like module that reads GLSL. I’d be happy to include this in the VCV Prototype plugin but maybe next year.

5 Likes

Hi @frank–welcome to the Rack forum! :slight_smile:

Agree with @Vortico that the prototyping function should be pretty different than plugin development and that C++ should remain the sole first-class language for actual Rack modules. Per that point, my alpha version of Faust integration reads the Faust GUI elements but only uses that to configure and bound CV inputs to the Faust kernel. The GUI is still up to the programmer (or fixed by the prototype plug).

It would definitely be possible to write a Faust transpiler that took the Faust GUI and set it up in Rack with the full Faust GUI complement, but, speaking for myself, I’m going to put my (unfortunately) limited time towards the prototype side first…

@modlfo wrote that LuaJIT is 15% slower than C++, so not 2-200x. I think this would be acceptable. I guess even 2x slower would be fine for most users. But power users with big patches could see a warning or some marker, that it is a scripted module.

Why do you think it is 20-60 MB per published module? The scripting engine could be included in the core framework. LuaJIT is less than one MB, and then maybe add another MB for the framework. Then the modules would be even smaller than when writing it in C++, and a plugin developer don’t have to install any development environment, which could be difficult depending on the OS. Just instantiate the scripting module, then specify the script file, or even allow to inline it in the VCV file. Security shouldn’t be a problem with Lua. But a user could get a warning when opening a VCV file with an embedded script and asked, if the script should be executed.

The API could have as much support as the full C++ API, there is no general limitation. I didn’t write it would be easy to implement it.

I agree that OpenGL is way to complicated and overkill for most users. But I think that a nice and small high-level API for quick prototyping graphical aspects would be appreciated by most plugin developers, even if they later use C++ to implement the “real” module. Really something simple, like load a SVG file, draw some lines and filled rectangles etc. Or maybe don’t reinvent the wheel and use something like Cairo: http://seriot.ch/pycairo/

Sure, you can embed a scripting language into your own plugin, but this is a different topic than VCV Prototype. I’m not interested in supporting a full non-C++ API.

I think it is necessary for useful prototyping to allow the user to create the UI elements with the script, because then the prototype would be exactly like the final module, when implemented later in C++. I understand that it would be a lot of work to implement and maintain in the VCV Rack code itself, so I guess it is up to module developers like me to do it in a module :slightly_smiling_face: I just did a quick hack to use LuaJIT to create inputs and outputs, and to process audio:


The compiled plugin is 216 kB (stripped). I used LuaJIT as a shared library. Might be better to link it as a static library, but I tried it and there was some error messages with fPIC etc., at which point I gave up to try this. But with the shared library it works. Link to the source code, including the echo script example, and compile instructions are in the video description. Tested for Linux only at the moment, but it should work on the other platforms as well, I didn’t use anything Linux special.
Would be nice to create the inputs and outputs without restarting Rack, but I’m not sure if this is possible. For example the config-call in the constructor of the module is supposed to be called only once. My hack to workaround this is to use a fixed max number of inputs and outputs, but would be better if this would be dynamic and controlled by the script.
3 Likes

another “hack” for configParam() would be to configure them with a set value, and do math to make those conform - set to 0-10 for instance, and if the prototype wants to set them -1' -1’ then simply do the math in the module before passing the value to the interpreter.

You’re free to use scripting languages for creating plugins, but this discussion is off-topic for this thread.

In your first posting in this thread you mentioned LuaJIT, and later you proposed the echo example, which I did, so I think it is on topic for this thread. Might not be the best Lua code, and the ringbuffer could be outsourced in an external Lua class and file to make it shorter and easier to read, but this is how it looks like with my Lua module:


Just ignore the input/output UI element creation code that you don’t want :upside_down_face:
I also tried to do a FFT, using this pure Lua implementation:

but this was really slow, unusable within the process function. But the code is not optimized. I think a proper Lua module should use Numeric Lua:
http://numlua.luaforge.net
This uses highly optimized C code from the Netlib library and BLAS and LAPACK for matrix operations, FFT etc., so it would be nearly as fast as everything written in C.
I guess it would be faster than Lua for heavy computational implementations inside Lua as well, if it doesn’t use the generic table for float arrays. @modlfo used in his tests, where he measured only 15% slower speed than C++, a special native double array as well.

Some examples in ChucK, for FFT/IFFT and Karplus-Strong see http://chuck.cs.princeton.edu/doc/examples

// attenuator
inputs[0] => outputs[0];
while (true) {
  1::sample => now;
  params[0] => outputs[0].gain;
}

// integrator
inputs[0] => blackhole;
Step step => outputs[0];

0.0 => float y;
now + delta => time later;
while (now < later) {
  y += inputs[0].last();
}
y => step.next;

// lfo
SinOsc osc => outputs[0];
440.0 => osc.freq;

SinOsc lfo1 => blackhole;
1.0/1::minute => lfo1.freq;
0.2 => lfo1.gain;

TriOsc lfo2 => blackhole;
1.0/3::minute => lfo2.freq;
0.1 => lfo2.gain;

while (true) {
  1::sample => now;
  lfo1.last() + osc.freq() => osc.freq;
  lfo2.last() + osc.phase() => osc.phase;
}

// filter
inputs[0] => HPF hpf => outputs[0];
4400.0 => hpf.freq;
0.9 => hpf.Q;

// echo
inputs[0] => Delay delay => outputs[0];
1::second => delay.max;
1::second => delay.delay;

Just when I was about to write a Faust prototype for Rack, I ran across this thread. I took a few of your hints and proudly present my take on this: https://github.com/mzuther/ProtoFaust. :slightly_smiling_face:

I had no time to write a README, yet, but I hope things are self-explaining. Just install a recent version of Faust, download the latest VCV Rack SDK and edit src/faust/main.dsp (the default is a simple three-oscillator synth with resonant filter). Then, hit make run and keep your fingers crossed…

Martin

Edit: updated link to match the renamed Git repository.

The name will be confusing if you release this and in 4-6 months there will be a “VCV Prototype”.
We’re waiting on @sempervirent to design a panel for this.

Hijack.

@sempervirent, I have a cat I would like to volunteer for your feline space program. Where can I sign her up?? One-way trips are OK.

2 Likes

I think your project is significantly different than the idea of this thread. It’s interesting that it works by compiling the Faust directly into the plugin. Could be used by people who can write C++ but would prefer a higher level language for DSP.

Hi Martin, testing this out. Pretty cool. Got the basic demo running. Any chance of showing how to change the Faust to run something like the “Guitar Effects Chain” on the Faust documentation site instead of or in addition to the resonant filter in the demo?

Thanks in advance.

Seems like I’ve somehow managed to miss the word “interpreted”. :roll_eyes:

My idea was to prototype and test by sending the file main.dsp to other people. In any case, it’s fun to using the project, and maybe it’s useful to someone.

Regarding the project’s name, I will change it as soon as I can think of a different one. Nobody is helped if people get confused.

I will be away for the week-end and still have to pack, so here’s a very short primer. The main process is

process(in1 , in2 , in3 , in4 , in5 , in6 , in7 , in8) = internal_processor
{ ... };

and you can simply put the guitar chain (or any other DSP process) in there:

 internal_processor = (in1 : gui_attacher) : guitar_chain : _ , in2 , in3 , in4 , in5 , in6 , in7 , in8 : si.bus(8);

Or in stereo:

 internal_processor = (in1 : gui_attacher) , in2 : guitar_chain : _ , _ , in3 , in4 , in5 , in6 , in7 , in8 : si.bus(8);

Just make sure that you add gui_attacher – this will attach the parameters and simply copy the mono input signal to its output. If you fail to do so, the Prototype module will not find any of the knobs, buttons and LEDs (they will be thought as superfluous and optimized out) and probably crash.

Good luck! :slight_smile:

Legend. Thanks.

How about Crossroads in reference to Robert Johnson’s supposed Faustian deal with the devil at the Crossroads, and your module is kind of a crossroads between c and Faust?

4 Likes

That’s really clever.

I agree! However, potential users might have difficulties in connecting the name with the module’s actual function.

As I like the theme of Mephisto, I have renamed the module to ProtoFaust (a reminiscence to Goethes “Urfaust”). Here’s the updated link: https://github.com/mzuther/ProtoFaust.

1 Like