Ideas for a Lua scripting module

Last month I did a quick proof of concept for a Lua module:

You can see the source code of the echo example here. This was just a quick hack, I want to discuss now how it should look like and what features do you want.

Some ideas what I think should be possible with this module:

  • custom module size and SVG
  • add GUI elements with the script: inputs, outputs, knobs, switches, push buttons, LEDs, labels etc.
  • process realtime audio, same as Module::process() in C++
  • visual updates, same as ModuleWidget::step() in C++
  • implementing your own widgets for drawing anything you like, for example as you can in the ScopeDisplay of Fundamental

Ideas for the API (unlike the VCV Rack API, the Lua script API should be object oriented and easy to use, even if it means it doesn’t run at maximum speed) :

One global Rack class as the interface to the Rack program, with the following functions:

  • Rack.setProcess(function): sets the process callback function, which is called at audio rate
  • Rack.setStep(function): sets the step callback function, which is called at video frame update rate
  • Rack.addLabel(x, y, text, size, [color]): adds a label
  • Rack.addInput(x, y): adds an input jack, returns an Input object. The Input object has the method getVoltage, with an optional parameter for the polyphonic input number.
  • Rack.addOutput(x, y): adds an output jack, returns an Output object. The Output object has the method setVoltage, with an optional parameter for the polyphonic output number.
  • Rack.addKnob(x, y, min, max, default): adds a knob, returns a Knob object. The Knob object has the method getValue(), which returns the current value of the knob.

So a simple sum script would look like this:

-- create the GUI
Rack.addLabel(50, 133, "-> Sum ->", 20)
inp1 = addInput(20, 120)
inp2 = addInput(20, 140)
out = addOutput(170, 130)

-- process function
function process()
    out:setVoltage(inp1:getVoltage() + inp2:getVoltage())

Rack.setProcess(process)

Note: In Lua the “:” syntax means it implicitly passes the “self” object to the method, so inp1.getVoltage(inp1) is the same as inp1:getVoltage(), but nicer to write and read. Rack has only one object, which means no need to pass a “self” object, think of it as static member functions if you know C++.

To organize the scripts I think it would be a good idea to have a directory for each script, in the script directory, within the Lua plugin directory. I think it would be good to have 3 files at least: module.lua, module.svg and module.json. All module.json files are scanned at start and provide a human readable name of the script module. A right click context menu allows you to select one of the scanned modules. When a module is loaded, the module.svg is displayed, the module is sized accordingly, and the module.lua script is executed. This means multiple independent Lua script module instances can be placed in Rack, with the same or different scripts running.

Don’t know how far this should go with automatic updates etc., but the module.json file could have a version tag as well and there could be a library page somewhere on the web to load Lua modules of other users, or later maybe even later to the VCV main homepage. I think to make it easier it is good to have one script directory for one module, no plugin hierarchy above it.

I think it would be nice if it reloads module.lua automatically when changed. This would make the development process very fast, good for quick experiments that don’t need max performance.

2 Likes

Hi Frank,

Could the following be possible in lua or your integration of it:

  1. load a custom json
  2. grab an array of numbers from a list in it
  3. use this array and output a random value from it on input trigger
  4. or go through the values based on a hardcoded int array that defines some kind of arpegiator sequence

I’d be really happy to code some kind of weird chord progression and/or base that whole thing on an external file + be able to still introduce randomness where and how I choose

ps: zero experience in lua, but I do have some in other OO languages

Good idea with json file reading, looks like there is already a json library for Lua. I think would be good to include some more libraries, like Numeric Lua. And then showing a warning on the panel when instantiating the module that you shouldn’t use any script from any source without looking at it first :slight_smile:

I gave it a try with my current old test implementation and inline arrays. Looks like this:

-- create GUI
addLabel(50, 124, "Trigger", 20)
inp = addInput(20, 120)
addLabel(180, 124, "Out", 20)
out = addOutput(150, 120)

-- init some variables
lastInput = 0
value = 0

-- pentatonic scale, G-flat major
values = {
    1.083, 1.250, 1.500, 1.667, 1.833,
    2.083, 2.250, 2.500, 2.667, 2.833
}

-- output random voltages of the scale on trigger
function process()
	v = getVoltage(inp)
    if lastInput == 0 and v > 0 then
        value = values[math.floor(math.random(#values))]
    end
    lastInput = v
    setVoltage(out, value)
end

and sounds like this:

The trigger is a bit simple, works only with perfect digital gate signals, should be a Schmitt-Trigger. I think this could be in an extra Lua library, which provides some useful utility classes.

2 Likes