Browse Source

Vult: Clean up code and auto-format.

tags/v1.3.0
Andrew Belt 4 years ago
parent
commit
c659480099
4 changed files with 139 additions and 220 deletions
  1. +2
    -2
      Makefile
  2. +5
    -4
      README.md
  3. +0
    -83
      examples/basic.vult
  4. +132
    -131
      src/VultEngine.cpp

+ 2
- 2
Makefile View File

@@ -22,8 +22,8 @@ VULT ?= 1

# Vult depends on both LuaJIT and QuickJS
ifeq ($(VULT), 1)
QUICKJS = 1
LUAJIT = 1
QUICKJS := 1
LUAJIT := 1
endif

# Entropia File System Watcher


+ 5
- 4
README.md View File

@@ -8,8 +8,8 @@ Scripting language host for [VCV Rack](https://vcvrack.com/) containing:
- 6 switches with RGB LEDs

Supported scripting languages:
- JavaScript (.js)
- Lua (.lua)
- JavaScript (ES2020) (.js)
- [Lua](https://www.lua.org/) (.lua)
- [Vult](https://github.com/modlfo/vult) (.vult)
- [Add your own below](#adding-a-script-engine)

@@ -91,7 +91,8 @@ function process(block) {
}
```

The Vult API is slightly different from the JavaScript version. Check the Vult examples included with the plugin to learn how to use the language with the VCV-Prototype plugin.
*The Vult API is slightly different than Prototype's scripting API.
See `examples/template.vult` for a reference of the Vult API.*

## Build dependencies

@@ -127,7 +128,7 @@ sudo pacman -S premake
## Contributors

- [Wes Milholen](https://grayscale.info/): panel design
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript, disabled), LuaJIT (Lua), Python (in development)
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript, not used), LuaJIT (Lua), Python (in development)
- [Jerry Sievert](https://github.com/JerrySievert): QuickJS (JavaScript)
- [Leonardo Laguna Ruiz](https://github.com/modlfo): Vult
- add your name here

+ 0
- 83
examples/basic.vult View File

@@ -1,83 +0,0 @@
/*
Vult API documentation.

Author: Leonardo Laguna Ruiz - leonardo@vult-dsp.com

The main difference of the Vult API compared to the JavaScript and Lua is that all interactions
happen through functions rather than accessing to the block arrays.

A Vult script requires the following two functions:

fun process() { }
and update() { }

The 'process' function is called every audio sample. As inputs, it will receive the values from
the input jacks but normalized to 1.0. This means that a value of 10.0 V in VCV Rack is received
as 1.0. Similarly, when you return a value of 1.0 it will be output by the prototype as 10.0V.

You can use the input and output jacks by adding or removing arguments to the function. For example,
to pass all the inputs to the outputs you can declare the function as follows:

fun process(i1, i2, i3, i4, i5, i6) {
return i1, i2, i3, i4, i5, i6;
}

The 'update' function is called once every 32 samples. You can use this function to perform actions
that do not require audio rate speed e.g. setting light colors or displying characters in the screen.
The function 'update' do not takes or returns any value.

Important: Notice that the 'update' function is declared with the keyword 'and'. In Vult language,
this means that they share context. At the moment, declaring them differently could have an undefined
behavior.

To interact with knobs, switches, lights the following builtin functions are provided.
NOTE: the knobs, switches and lights are numbered from 1 to 6

getKnob(n:int) : real // value of the nth knob range: 0.0-1.0
getSwitch(n:int) : bool // value of the nth switch: true/false

setLight(n:int, r:real, g:real, b:real) // r, g, b range: 0.0-1.0
setSwitchLight(n:int, r:real, g:real, b:real) // r, g, b range: 0.0-1.0

samplerate() : real // current sample rate
sampletime() : real // current time step (1.0 / samplerate())
display(text:string) // display text in the screen

*/


// Returns the r,g,b values for a given voltage
fun getRGB(v) {
if (v > 0.0)
return v, 0.0, 0.0;
else
return 0.0, -v, 0.0;
}

// Takes two inputs and returns the result of different operations on them
fun process(in1, in2) {
// theses are declared as 'mem' so we can remember them and use them in 'update'
mem sum = clip(in1 + in2, -1.0, 1.0); // use 'clip' to keep the signals in the specified range
mem sub = clip(in1 - in2, -1.0, 1.0);
mem mul = clip(in1 * in2, -1.0, 1.0);
return sum, sub, mul;
}
and update() {
_ = display("Add two LFO to IN1 and IN2");
val r, g, b;
// Set the light no 1 with the 'sum' value
r, g, b = getRGB(sum);
_ = setLight(1, r, g, b);
_ = setSwitchLight(1, r, g, b);

// Set the light no 2 with the 'sub' value
r, g, b = getRGB(sub);
_ = setLight(2, r, g, b);
_ = setSwitchLight(2, r, g, b);

// Set the light no 2 with the 'mul' value
r, g, b = getRGB(mul);
_ = setLight(3, r, g, b);
_ = setSwitchLight(3, r, g, b);

}

+ 132
- 131
src/VultEngine.cpp View File

@@ -9,145 +9,146 @@
* compiler generates Lua code that is executed by the LuaJIT engine.
*/

extern std::map<std::string, ScriptEngineFactory *> scriptEngineFactories;
// Special version of createScriptEngine that only creates Lua engines
ScriptEngine *createLuaEngine() {
auto it = scriptEngineFactories.find("lua");
if (it == scriptEngineFactories.end())
return NULL;
return it->second->createScriptEngine();
ScriptEngine* createLuaEngine() {
auto it = scriptEngineFactories.find("lua");
if (it == scriptEngineFactories.end())
return NULL;
return it->second->createScriptEngine();
}

struct VultEngine : ScriptEngine {

// used to run the lua generated code
ScriptEngine *luaEngine;

// used to run the Vult compiler
JSRuntime *rt = NULL;
JSContext *ctx = NULL;

VultEngine() {
rt = JS_NewRuntime();
// Create QuickJS context
ctx = JS_NewContext(rt);
if (!ctx) {
display("Could not create QuickJS context");
return;
}

JSValue global_obj = JS_GetGlobalObject(ctx);

// Load the Vult compiler code
JSValue val =
JS_Eval(ctx, (const char *)vultc_h, vultc_h_size, "vultc.js", 0);
if (JS_IsException(val)) {
display("Error loading the Vult compiler");
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, global_obj);
return;
}
}

~VultEngine() {
if (ctx) {
JS_FreeContext(ctx);
}
if (rt) {
JS_FreeRuntime(rt);
}
}

std::string getEngineName() override { return "Vult"; }

int run(const std::string &path, const std::string &script) override {
display("Loading...");

JSValue global_obj = JS_GetGlobalObject(ctx);

// Put the script text in the 'code' variable
JSValue code = JS_NewString(ctx, script.c_str());
JS_SetPropertyStr(ctx, global_obj, "code", code);

// Put the script path in 'file' variable
JSValue file = JS_NewString(ctx, path.c_str());
JS_SetPropertyStr(ctx, global_obj, "file", file);

display("Compiling...");
// Call the Vult compiler to generate Lua code
static const std::string testVult = R"(
// used to run the lua generated code
ScriptEngine* luaEngine;

// used to run the Vult compiler
JSRuntime* rt = NULL;
JSContext* ctx = NULL;

VultEngine() {
rt = JS_NewRuntime();
// Create QuickJS context
ctx = JS_NewContext(rt);
if (!ctx) {
display("Could not create QuickJS context");
return;
}

JSValue global_obj = JS_GetGlobalObject(ctx);

// Load the Vult compiler code
JSValue val =
JS_Eval(ctx, (const char*)vultc_h, vultc_h_size, "vultc.js", 0);
if (JS_IsException(val)) {
display("Error loading the Vult compiler");
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, global_obj);
return;
}
}

~VultEngine() {
if (ctx) {
JS_FreeContext(ctx);
}
if (rt) {
JS_FreeRuntime(rt);
}
}

std::string getEngineName() override {
return "Vult";
}

int run(const std::string& path, const std::string& script) override {
display("Loading...");

JSValue global_obj = JS_GetGlobalObject(ctx);

// Put the script text in the 'code' variable
JSValue code = JS_NewString(ctx, script.c_str());
JS_SetPropertyStr(ctx, global_obj, "code", code);

// Put the script path in 'file' variable
JSValue file = JS_NewString(ctx, path.c_str());
JS_SetPropertyStr(ctx, global_obj, "file", file);

display("Compiling...");
// Call the Vult compiler to generate Lua code
static const std::string testVult = R"(
var result = vult.generateLua([{ file:file, code:code}],{ output:'Engine', template:'vcv-prototype'});)";

JSValue compile =
JS_Eval(ctx, testVult.c_str(), testVult.size(), "Compile", 0);

JS_FreeValue(ctx, code);
JS_FreeValue(ctx, file);

// If there are any internal errors, the execution could fail
if (JS_IsException(compile)) {
display("Fatal error in the Vult compiler");
JS_FreeValue(ctx, global_obj);
return -1;
}

// Retrive the variable 'result'
JSValue result = JS_GetPropertyStr(ctx, global_obj, "result");
// Get the first element of the 'result' array
JSValue first = JS_GetPropertyUint32(ctx, result, 0);
// Try to get the 'msg' field which is only present in error messages
JSValue msg = JS_GetPropertyStr(ctx, first, "msg");
// Display the error if any
if (!JS_IsUndefined(msg)) {
const char *text = JS_ToCString(ctx, msg);
const char *row =
JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "line"));
const char *col = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "col"));
// Compose the error message
std::stringstream error;
error << "line:" << row << ":" << col << ": " << text;
WARN("Vult Error: %s", error.str().c_str());
display(error.str().c_str());

JS_FreeValue(ctx, result);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
return -1;
}
// In case of no error, retrieve the generated code
JSValue luacode = JS_GetPropertyStr(ctx, first, "code");
std::string luacode_str(JS_ToCString(ctx, luacode));

//WARN("Generated Code: %s", luacode_str.c_str());

luaEngine = createLuaEngine();

if (!luaEngine) {
WARN("Could not create a Lua script engine");
JSValue compile =
JS_Eval(ctx, testVult.c_str(), testVult.size(), "Compile", 0);

JS_FreeValue(ctx, code);
JS_FreeValue(ctx, file);

// If there are any internal errors, the execution could fail
if (JS_IsException(compile)) {
display("Fatal error in the Vult compiler");
JS_FreeValue(ctx, global_obj);
return -1;
}

// Retrive the variable 'result'
JSValue result = JS_GetPropertyStr(ctx, global_obj, "result");
// Get the first element of the 'result' array
JSValue first = JS_GetPropertyUint32(ctx, result, 0);
// Try to get the 'msg' field which is only present in error messages
JSValue msg = JS_GetPropertyStr(ctx, first, "msg");
// Display the error if any
if (!JS_IsUndefined(msg)) {
const char* text = JS_ToCString(ctx, msg);
const char* row =
JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "line"));
const char* col = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "col"));
// Compose the error message
std::stringstream error;
error << "line:" << row << ":" << col << ": " << text;
WARN("Vult Error: %s", error.str().c_str());
display(error.str().c_str());

JS_FreeValue(ctx, result);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
return -1;
}
// In case of no error, retrieve the generated code
JSValue luacode = JS_GetPropertyStr(ctx, first, "code");
std::string luacode_str(JS_ToCString(ctx, luacode));

//WARN("Generated Code: %s", luacode_str.c_str());

luaEngine = createLuaEngine();

if (!luaEngine) {
WARN("Could not create a Lua script engine");
return -1;
}

luaEngine->module = this->module;

display("Running...");

JS_FreeValue(ctx, luacode);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, global_obj);

return luaEngine->run(path, luacode_str);
}

int process() override {
if (!luaEngine)
return -1;
}

luaEngine->module = this->module;

display("Running...");

JS_FreeValue(ctx, luacode);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, global_obj);

return luaEngine->run(path, luacode_str);
}

int process() override {
if (luaEngine)
return luaEngine->process();
else
return 0;
}
return luaEngine->process();
}
};

__attribute__((constructor(1000))) static void constructor() {
addScriptEngine<VultEngine>("vult");
__attribute__((constructor(1000)))
static void constructor() {
addScriptEngine<VultEngine>("vult");
}

Loading…
Cancel
Save