@@ -16,6 +16,7 @@ include $(RACK_DIR)/arch.mk | |||||
DUKTAPE ?= 0 | DUKTAPE ?= 0 | ||||
QUICKJS ?= 1 | QUICKJS ?= 1 | ||||
PYTHON ?= 1 | PYTHON ?= 1 | ||||
LUA ?= 1 | |||||
# Entropia File System Watcher | # Entropia File System Watcher | ||||
efsw := dep/lib/libefsw-static-release.a | efsw := dep/lib/libefsw-static-release.a | ||||
@@ -97,14 +98,19 @@ $(numpy): $(python) | |||||
# cd dep/scipy-1.3.1 && "$(DEP_PATH)"/bin/python3.7 setup.py build -j4 install | # cd dep/scipy-1.3.1 && "$(DEP_PATH)"/bin/python3.7 setup.py build -j4 install | ||||
endif | endif | ||||
# # LuaJIT | |||||
# luajit := dep/lib/luajit.a | |||||
# DEPS += $(luajit) | |||||
# $(luajit): | |||||
# cd dep && $(WGET) "http://luajit.org/download/LuaJIT-2.0.5.tar.gz" | |||||
# cd dep && $(SHA256) LuaJIT-2.0.5.tar.gz 874b1f8297c697821f561f9b73b57ffd419ed8f4278c82e05b48806d30c1e979 | |||||
# cd dep && $(UNTAR) LuaJIT-2.0.5.tar.gz | |||||
# cd dep/LuaJIT-2.0.5 && $(MAKE) | |||||
# LuaJIT | |||||
ifeq ($(LUA), 1) | |||||
SOURCES += src/LuaJITEngine.cpp | |||||
luajit := dep/lib/libluajit-5.1.a | |||||
OBJECTS += $(luajit) | |||||
DEPS += $(luajit) | |||||
$(luajit): | |||||
$(WGET) "http://luajit.org/download/LuaJIT-2.0.5.tar.gz" | |||||
$(SHA256) LuaJIT-2.0.5.tar.gz 874b1f8297c697821f561f9b73b57ffd419ed8f4278c82e05b48806d30c1e979 | |||||
cd dep && $(UNTAR) ../LuaJIT-2.0.5.tar.gz | |||||
cd dep/LuaJIT-2.0.5 && $(MAKE) | |||||
cd dep/LuaJIT-2.0.5 && $(MAKE) PREFIX="$(DEP_PATH)" install | |||||
endif | |||||
# # Julia | # # Julia | ||||
# julia := dep/lib/libjulia.a | # julia := dep/lib/libjulia.a | ||||
@@ -124,6 +124,6 @@ sudo pacman -S premake | |||||
## Contributors | ## Contributors | ||||
- [Wes Milholen](https://grayscale.info/): panel design | - [Wes Milholen](https://grayscale.info/): panel design | ||||
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript) | |||||
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript), LuaJIT (in development), Python (in development) | |||||
- [Jerry Sievert](https://github.com/JerrySievert): QuickJS (JavaScript) | - [Jerry Sievert](https://github.com/JerrySievert): QuickJS (JavaScript) | ||||
- add your name here | - add your name here |
@@ -3,13 +3,13 @@ | |||||
function process(block) { | function process(block) { | ||||
// Loop through each column | // Loop through each column | ||||
for (var i = 0; i < 6; i++) { | |||||
for (let i = 0; i < 6; i++) { | |||||
// Get input | // Get input | ||||
var x = block.inputs[i][0] | |||||
let x = block.inputs[i][0] | |||||
// Get gain knob | // Get gain knob | ||||
var gain = block.knobs[i] | |||||
let gain = block.knobs[i] | |||||
// Apply gain to input | // Apply gain to input | ||||
var y = x * gain | |||||
let y = x * gain | |||||
// Set gain light (red = 0) | // Set gain light (red = 0) | ||||
block.lights[i][0] = gain | block.lights[i][0] = gain | ||||
// Check mute switch | // Check mute switch | ||||
@@ -0,0 +1,28 @@ | |||||
-- Simplest possible script using all variables | |||||
-- by Andrew Belt | |||||
function process(block) | |||||
-- Loop through each column | |||||
for i=1,6 do | |||||
-- Get input | |||||
x = block.inputs[i][1] | |||||
-- Get gain knob | |||||
gain = block.knobs[i] | |||||
-- Apply gain to input | |||||
y = x * gain | |||||
-- Set gain light (red = 0) | |||||
block.lights[i][1] = gain | |||||
-- Check mute switch | |||||
if block.switches[i] then | |||||
-- Mute output | |||||
y = 0 | |||||
-- Enable mute light (red = 0) | |||||
block.switchLights[i][1] = 1 | |||||
else | |||||
-- Disable mute light | |||||
block.switchLights[i][1] = 0 | |||||
end | |||||
-- Set output | |||||
block.outputs[i][1] = y | |||||
end | |||||
end |
@@ -0,0 +1,44 @@ | |||||
-- Rainbow RGB LED example | |||||
-- by Andrew Belt | |||||
-- Call process() every 256 audio samples | |||||
config.frameDivider = 256 | |||||
-- From https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB | |||||
function hsvToRgb(h, s, v) | |||||
h = h * 6 | |||||
c = v * s | |||||
x = c * (1 - math.abs(h % 2 - 1)) | |||||
if (h < 1) then rgb = {c, x, 0} | |||||
elseif (h < 2) then rgb = {x, c, 0} | |||||
elseif (h < 3) then rgb = {0, c, x} | |||||
elseif (h < 4) then rgb = {0, x, c} | |||||
elseif (h < 5) then rgb = {x, 0, c} | |||||
else rgb = {c, 0, x} | |||||
end | |||||
m = v - c | |||||
rgb[1] = rgb[1] + m | |||||
rgb[2] = rgb[2] + m | |||||
rgb[3] = rgb[3] + m | |||||
return rgb | |||||
end | |||||
phase = 0 | |||||
function process(block) | |||||
phase = phase + block.sampleTime * config.frameDivider * 0.5 | |||||
phase = phase % 1 | |||||
for i=1,6 do | |||||
h = (1 - i / 6 + phase) % 1 | |||||
rgb = hsvToRgb(h, 1, 1) | |||||
for c=1,3 do | |||||
block.lights[i][c] = rgb[c] | |||||
block.switchLights[i][c] = rgb[c] | |||||
end | |||||
block.outputs[i][1] = math.sin(2 * math.pi * h) * 5 + 5 | |||||
end | |||||
end | |||||
display("Hello, world!") |
@@ -16,17 +16,19 @@ struct DuktapeEngine : ScriptEngine { | |||||
int run(const std::string& path, const std::string& script) override { | int run(const std::string& path, const std::string& script) override { | ||||
assert(!ctx); | assert(!ctx); | ||||
ProcessBlock* block = getProcessBlock(); | |||||
// Create duktape context | // Create duktape context | ||||
ctx = duk_create_heap_default(); | ctx = duk_create_heap_default(); | ||||
if (!ctx) { | if (!ctx) { | ||||
setMessage("Could not create duktape context"); | |||||
display("Could not create duktape context"); | |||||
return -1; | return -1; | ||||
} | } | ||||
// Initialize globals | // Initialize globals | ||||
// user pointer | // user pointer | ||||
duk_push_pointer(ctx, this); | duk_push_pointer(ctx, this); | ||||
duk_put_global_string(ctx, DUK_HIDDEN_SYMBOL("p")); | |||||
duk_put_global_string(ctx, DUK_HIDDEN_SYMBOL("engine")); | |||||
// console | // console | ||||
duk_idx_t consoleIdx = duk_push_object(ctx); | duk_idx_t consoleIdx = duk_push_object(ctx); | ||||
@@ -34,15 +36,6 @@ struct DuktapeEngine : ScriptEngine { | |||||
// log | // log | ||||
duk_push_c_function(ctx, native_console_log, 1); | duk_push_c_function(ctx, native_console_log, 1); | ||||
duk_put_prop_string(ctx, consoleIdx, "log"); | duk_put_prop_string(ctx, consoleIdx, "log"); | ||||
// info (alias for log) | |||||
duk_push_c_function(ctx, native_console_log, 1); | |||||
duk_put_prop_string(ctx, consoleIdx, "info"); | |||||
// debug | |||||
duk_push_c_function(ctx, native_console_debug, 1); | |||||
duk_put_prop_string(ctx, consoleIdx, "debug"); | |||||
// warn | |||||
duk_push_c_function(ctx, native_console_warn, 1); | |||||
duk_put_prop_string(ctx, consoleIdx, "warn"); | |||||
} | } | ||||
duk_put_global_string(ctx, "console"); | duk_put_global_string(ctx, "console"); | ||||
@@ -67,7 +60,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
if (duk_pcompile_lstring_filename(ctx, 0, script.c_str(), script.size()) != 0) { | if (duk_pcompile_lstring_filename(ctx, 0, script.c_str(), script.size()) != 0) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
WARN("duktape: %s", s); | WARN("duktape: %s", s); | ||||
setMessage(s); | |||||
display(s); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
return -1; | return -1; | ||||
} | } | ||||
@@ -75,7 +68,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
if (duk_pcall(ctx, 0)) { | if (duk_pcall(ctx, 0)) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
WARN("duktape: %s", s); | WARN("duktape: %s", s); | ||||
setMessage(s); | |||||
display(s); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
return -1; | return -1; | ||||
} | } | ||||
@@ -91,7 +84,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
// bufferSize | // bufferSize | ||||
duk_get_prop_string(ctx, -1, "bufferSize"); | duk_get_prop_string(ctx, -1, "bufferSize"); | ||||
block->bufferSize = duk_get_int(ctx, -1); | |||||
setBufferSize(duk_get_int(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
@@ -99,7 +92,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
// Keep process function on stack for faster calling | // Keep process function on stack for faster calling | ||||
duk_get_global_string(ctx, "process"); | duk_get_global_string(ctx, "process"); | ||||
if (!duk_is_function(ctx, -1)) { | if (!duk_is_function(ctx, -1)) { | ||||
setMessage("No process() function"); | |||||
display("No process() function"); | |||||
return -1; | return -1; | ||||
} | } | ||||
@@ -170,6 +163,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
int process() override { | int process() override { | ||||
// block | // block | ||||
ProcessBlock* block = getProcessBlock(); | |||||
duk_idx_t blockIdx = duk_get_top(ctx) - 1; | duk_idx_t blockIdx = duk_get_top(ctx) - 1; | ||||
{ | { | ||||
// sampleRate | // sampleRate | ||||
@@ -193,7 +187,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
if (duk_pcall(ctx, 1)) { | if (duk_pcall(ctx, 1)) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
WARN("duktape: %s", s); | WARN("duktape: %s", s); | ||||
setMessage(s); | |||||
display(s); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
return -1; | return -1; | ||||
} | } | ||||
@@ -204,7 +198,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
} | } | ||||
static DuktapeEngine* getDuktapeEngine(duk_context* ctx) { | static DuktapeEngine* getDuktapeEngine(duk_context* ctx) { | ||||
duk_get_global_string(ctx, DUK_HIDDEN_SYMBOL("p")); | |||||
duk_get_global_string(ctx, DUK_HIDDEN_SYMBOL("engine")); | |||||
DuktapeEngine* engine = (DuktapeEngine*) duk_get_pointer(ctx, -1); | DuktapeEngine* engine = (DuktapeEngine*) duk_get_pointer(ctx, -1); | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
return engine; | return engine; | ||||
@@ -212,22 +206,12 @@ struct DuktapeEngine : ScriptEngine { | |||||
static duk_ret_t native_console_log(duk_context* ctx) { | static duk_ret_t native_console_log(duk_context* ctx) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
INFO("VCV Prototype: %s", s); | |||||
return 0; | |||||
} | |||||
static duk_ret_t native_console_debug(duk_context* ctx) { | |||||
const char* s = duk_safe_to_string(ctx, -1); | |||||
DEBUG("VCV Prototype: %s", s); | |||||
return 0; | |||||
} | |||||
static duk_ret_t native_console_warn(duk_context* ctx) { | |||||
const char* s = duk_safe_to_string(ctx, -1); | |||||
WARN("VCV Prototype: %s", s); | |||||
INFO("Duktape: %s", s); | |||||
return 0; | return 0; | ||||
} | } | ||||
static duk_ret_t native_display(duk_context* ctx) { | static duk_ret_t native_display(duk_context* ctx) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
getDuktapeEngine(ctx)->setMessage(s); | |||||
getDuktapeEngine(ctx)->display(s); | |||||
return 0; | return 0; | ||||
} | } | ||||
}; | }; | ||||
@@ -0,0 +1,300 @@ | |||||
#include "ScriptEngine.hpp" | |||||
#include <luajit-2.0/lua.hpp> | |||||
struct LuaJITEngine : ScriptEngine { | |||||
lua_State* L = NULL; | |||||
~LuaJITEngine() { | |||||
if (L) | |||||
lua_close(L); | |||||
} | |||||
std::string getEngineName() override { | |||||
return "Lua"; | |||||
} | |||||
int run(const std::string& path, const std::string& script) override { | |||||
ProcessBlock* block = getProcessBlock(); | |||||
L = luaL_newstate(); | |||||
if (!L) { | |||||
display("Could not create LuaJIT context"); | |||||
return -1; | |||||
} | |||||
// Import a subset of the standard library | |||||
luaopen_base(L); | |||||
luaopen_string(L); | |||||
luaopen_table(L); | |||||
luaopen_math(L); | |||||
// Set user pointer | |||||
lua_pushlightuserdata(L, this); | |||||
lua_setglobal(L, "_engine"); | |||||
// Set global functions | |||||
lua_pushcfunction(L, native_print); | |||||
lua_setglobal(L, "print"); | |||||
lua_pushcfunction(L, native_display); | |||||
lua_setglobal(L, "display"); | |||||
// Set config | |||||
lua_newtable(L); | |||||
{ | |||||
// frameDivider | |||||
lua_pushinteger(L, 32); | |||||
lua_setfield(L, -2, "frameDivider"); | |||||
// bufferSize | |||||
lua_pushinteger(L, 1); | |||||
lua_setfield(L, -2, "bufferSize"); | |||||
} | |||||
lua_setglobal(L, "config"); | |||||
// Compile script | |||||
if (luaL_loadbuffer(L, script.c_str(), script.size(), path.c_str())) { | |||||
const char* s = lua_tostring(L, -1); | |||||
WARN("LuaJIT: %s", s); | |||||
display(s); | |||||
lua_pop(L, 1); | |||||
return -1; | |||||
} | |||||
// Run script | |||||
if (lua_pcall(L, 0, 0, 0)) { | |||||
const char* s = lua_tostring(L, -1); | |||||
WARN("LuaJIT: %s", s); | |||||
display(s); | |||||
lua_pop(L, 1); | |||||
return -1; | |||||
} | |||||
// Get config | |||||
lua_getglobal(L, "config"); | |||||
{ | |||||
// frameDivider | |||||
lua_getfield(L, -1, "frameDivider"); | |||||
int frameDivider = lua_tointeger(L, -1); | |||||
setFrameDivider(frameDivider); | |||||
lua_pop(L, 1); | |||||
// bufferSize | |||||
lua_getfield(L, -1, "bufferSize"); | |||||
int bufferSize = lua_tointeger(L, -1); | |||||
setBufferSize(bufferSize); | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// Get process function | |||||
lua_getglobal(L, "process"); | |||||
if (!lua_isfunction(L, -1)) { | |||||
display("No process() function"); | |||||
return -1; | |||||
} | |||||
// Create block object | |||||
lua_newtable(L); | |||||
{ | |||||
// inputs | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_newtable(L); | |||||
for (int j = 0; j < block->bufferSize; j++) { | |||||
lua_pushnumber(L, 0.0); | |||||
lua_rawseti(L, -2, j + 1); | |||||
} | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "inputs"); | |||||
// outputs | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_newtable(L); | |||||
for (int j = 0; j < block->bufferSize; j++) { | |||||
lua_pushnumber(L, 0.0); | |||||
lua_rawseti(L, -2, j + 1); | |||||
} | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "outputs"); | |||||
// knobs | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_pushnumber(L, 0.0); | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "knobs"); | |||||
// switches | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_pushboolean(L, false); | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "switches"); | |||||
// lights | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_newtable(L); | |||||
for (int c = 0; c < 3; c++) { | |||||
lua_pushnumber(L, 0.0); | |||||
lua_rawseti(L, -2, c + 1); | |||||
} | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "lights"); | |||||
// switchLights | |||||
lua_newtable(L); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_newtable(L); | |||||
for (int c = 0; c < 3; c++) { | |||||
lua_pushnumber(L, 0.0); | |||||
lua_rawseti(L, -2, c + 1); | |||||
} | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_setfield(L, -2, "switchLights"); | |||||
} | |||||
return 0; | |||||
} | |||||
int process() override { | |||||
ProcessBlock* block = getProcessBlock(); | |||||
// Set block | |||||
{ | |||||
// sampleRate | |||||
lua_pushnumber(L, block->sampleRate); | |||||
lua_setfield(L, -2, "sampleRate"); | |||||
// sampleTime | |||||
lua_pushnumber(L, block->sampleTime); | |||||
lua_setfield(L, -2, "sampleTime"); | |||||
// bufferSize | |||||
lua_pushinteger(L, block->bufferSize); | |||||
lua_setfield(L, -2, "bufferSize"); | |||||
// inputs | |||||
lua_getfield(L, -1, "inputs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_rawgeti(L, -1, i + 1); | |||||
for (int j = 0; j < block->bufferSize; j++) { | |||||
lua_pushnumber(L, block->inputs[i][j]); | |||||
lua_rawseti(L, -2, j + 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// knobs | |||||
lua_getfield(L, -1, "knobs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_pushnumber(L, block->knobs[i]); | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// switches | |||||
lua_getfield(L, -1, "switches"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_pushboolean(L, block->switches[i]); | |||||
lua_rawseti(L, -2, i + 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
// Duplicate process function | |||||
lua_pushvalue(L, -2); | |||||
// Duplicate block | |||||
lua_pushvalue(L, -2); | |||||
// Call process function | |||||
if (lua_pcall(L, 1, 0, 0)) { | |||||
const char* err = lua_tostring(L, -1); | |||||
WARN("LuaJIT: %s", err); | |||||
display(err); | |||||
return -1; | |||||
} | |||||
// Get block | |||||
{ | |||||
// outputs | |||||
lua_getfield(L, -1, "outputs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_rawgeti(L, -1, i + 1); | |||||
for (int j = 0; j < block->bufferSize; j++) { | |||||
lua_rawgeti(L, -1, j + 1); | |||||
block->outputs[i][j] = lua_tonumber(L, -1); | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// knobs | |||||
lua_getfield(L, -1, "knobs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_rawgeti(L, -1, i + 1); | |||||
block->knobs[i] = lua_tonumber(L, -1); | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// lights | |||||
lua_getfield(L, -1, "lights"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_rawgeti(L, -1, i + 1); | |||||
for (int c = 0; c < 3; c++) { | |||||
lua_rawgeti(L, -1, c + 1); | |||||
block->lights[i][c] = lua_tonumber(L, -1); | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
// switchLights | |||||
lua_getfield(L, -1, "switchLights"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | |||||
lua_rawgeti(L, -1, i + 1); | |||||
for (int c = 0; c < 3; c++) { | |||||
lua_rawgeti(L, -1, c + 1); | |||||
block->switchLights[i][c] = lua_tonumber(L, -1); | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
lua_pop(L, 1); | |||||
} | |||||
return 0; | |||||
} | |||||
static LuaJITEngine* getEngine(lua_State* L) { | |||||
lua_getglobal(L, "_engine"); | |||||
LuaJITEngine* engine = (LuaJITEngine*) lua_touserdata(L, -1); | |||||
lua_pop(L, 1); | |||||
return engine; | |||||
} | |||||
static int native_print(lua_State* L) { | |||||
lua_getglobal(L, "tostring"); | |||||
lua_pushvalue(L, 1); | |||||
lua_call(L, 1, 1); | |||||
const char* s = lua_tostring(L, 1); | |||||
INFO("LuaJIT: %s", s); | |||||
return 0; | |||||
} | |||||
static int native_display(lua_State* L) { | |||||
lua_getglobal(L, "tostring"); | |||||
lua_pushvalue(L, 1); | |||||
lua_call(L, 1, 1); | |||||
const char* s = lua_tostring(L, -1); | |||||
if (!s) | |||||
s = "(null)"; | |||||
getEngine(L)->display(s); | |||||
return 0; | |||||
} | |||||
}; | |||||
ScriptEngine* createLuaJITEngine() { | |||||
return new LuaJITEngine; | |||||
} |
@@ -41,7 +41,7 @@ struct Prototype : Module { | |||||
ScriptEngine* scriptEngine = NULL; | ScriptEngine* scriptEngine = NULL; | ||||
int frame = 0; | int frame = 0; | ||||
int frameDivider; | int frameDivider; | ||||
ScriptEngine::ProcessBlock* block; | |||||
ProcessBlock* block; | |||||
int bufferIndex = 0; | int bufferIndex = 0; | ||||
efsw_watcher efsw = NULL; | efsw_watcher efsw = NULL; | ||||
@@ -53,7 +53,7 @@ struct Prototype : Module { | |||||
for (int i = 0; i < NUM_ROWS; i++) | for (int i = 0; i < NUM_ROWS; i++) | ||||
configParam(SWITCH_PARAMS + i, 0.f, 1.f, 0.f, string::f("Switch %d", i + 1)); | configParam(SWITCH_PARAMS + i, 0.f, 1.f, 0.f, string::f("Switch %d", i + 1)); | ||||
block = new ScriptEngine::ProcessBlock; | |||||
block = new ProcessBlock; | |||||
setPath(""); | setPath(""); | ||||
} | } | ||||
@@ -179,7 +179,7 @@ struct Prototype : Module { | |||||
// Reset process state | // Reset process state | ||||
frameDivider = 32; | frameDivider = 32; | ||||
frame = 0; | frame = 0; | ||||
*block = ScriptEngine::ProcessBlock(); | |||||
*block = ProcessBlock(); | |||||
bufferIndex = 0; | bufferIndex = 0; | ||||
// Reset outputs and lights because they might hold old values | // Reset outputs and lights because they might hold old values | ||||
for (int i = 0; i < NUM_ROWS; i++) | for (int i = 0; i < NUM_ROWS; i++) | ||||
@@ -203,7 +203,6 @@ struct Prototype : Module { | |||||
return; | return; | ||||
} | } | ||||
scriptEngine->module = this; | scriptEngine->module = this; | ||||
scriptEngine->block = block; | |||||
// Run script | // Run script | ||||
if (scriptEngine->run(path, script)) { | if (scriptEngine->run(path, script)) { | ||||
@@ -212,7 +211,6 @@ struct Prototype : Module { | |||||
scriptEngine = NULL; | scriptEngine = NULL; | ||||
return; | return; | ||||
} | } | ||||
block->bufferSize = clamp(block->bufferSize, 1, MAX_BUFFER_SIZE); | |||||
this->engineName = scriptEngine->getEngineName(); | this->engineName = scriptEngine->getEngineName(); | ||||
} | } | ||||
@@ -296,12 +294,19 @@ struct Prototype : Module { | |||||
}; | }; | ||||
void ScriptEngine::setMessage(const std::string& message) { | |||||
void ScriptEngine::display(const std::string& message) { | |||||
module->message = message; | module->message = message; | ||||
} | } | ||||
void ScriptEngine::setFrameDivider(int frameDivider) { | void ScriptEngine::setFrameDivider(int frameDivider) { | ||||
module->frameDivider = frameDivider; | module->frameDivider = frameDivider; | ||||
} | } | ||||
void ScriptEngine::setBufferSize(int bufferSize) { | |||||
bufferSize = clamp(bufferSize, 1, MAX_BUFFER_SIZE); | |||||
module->block->bufferSize = bufferSize; | |||||
} | |||||
ProcessBlock* ScriptEngine::getProcessBlock() { | |||||
return module->block; | |||||
} | |||||
struct FileChoice : LedDisplayChoice { | struct FileChoice : LedDisplayChoice { | ||||
@@ -103,6 +103,7 @@ struct PythonEngine : ScriptEngine { | |||||
DEFER({Py_DECREF(result);}); | DEFER({Py_DECREF(result);}); | ||||
// Create block | // Create block | ||||
ProcessBlock* block = getProcessBlock(); | |||||
static PyStructSequence_Field blockFields[] = { | static PyStructSequence_Field blockFields[] = { | ||||
{"inputs", ""}, | {"inputs", ""}, | ||||
{"outputs", ""}, | {"outputs", ""}, | ||||
@@ -154,11 +155,11 @@ struct PythonEngine : ScriptEngine { | |||||
// Get process function from globals | // Get process function from globals | ||||
processFunc = PyDict_GetItemString(mainDict, "process"); | processFunc = PyDict_GetItemString(mainDict, "process"); | ||||
if (!processFunc) { | if (!processFunc) { | ||||
setMessage("No process() function"); | |||||
display("No process() function"); | |||||
return -1; | return -1; | ||||
} | } | ||||
if (!PyCallable_Check(processFunc)) { | if (!PyCallable_Check(processFunc)) { | ||||
setMessage("process() is not callable"); | |||||
display("process() is not callable"); | |||||
return -1; | return -1; | ||||
} | } | ||||
@@ -181,7 +182,7 @@ struct PythonEngine : ScriptEngine { | |||||
// if (!str) | // if (!str) | ||||
// return -1; | // return -1; | ||||
// setMessage(str); | |||||
// display(str); | |||||
return -1; | return -1; | ||||
} | } | ||||
DEFER({Py_DECREF(processResult);}); | DEFER({Py_DECREF(processResult);}); | ||||
@@ -209,7 +210,7 @@ struct PythonEngine : ScriptEngine { | |||||
DEFER({Py_DECREF(msgS);}); | DEFER({Py_DECREF(msgS);}); | ||||
const char* msg = PyUnicode_AsUTF8(msgS); | const char* msg = PyUnicode_AsUTF8(msgS); | ||||
that->setMessage(msg); | |||||
that->display(msg); | |||||
Py_INCREF(Py_None); | Py_INCREF(Py_None); | ||||
return Py_None; | return Py_None; | ||||
@@ -65,7 +65,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
// Create quickjs context | // Create quickjs context | ||||
ctx = JS_NewContext(rt); | ctx = JS_NewContext(rt); | ||||
if (!ctx) { | if (!ctx) { | ||||
setMessage("Could not create QuickJS context"); | |||||
display("Could not create QuickJS context"); | |||||
return -1; | return -1; | ||||
} | } | ||||
@@ -107,13 +107,14 @@ struct QuickJSEngine : ScriptEngine { | |||||
// Compile string | // Compile string | ||||
JSValue val = JS_Eval(ctx, script.c_str(), script.size(), path.c_str(), 0); | JSValue val = JS_Eval(ctx, script.c_str(), script.size(), path.c_str(), 0); | ||||
if (JS_IsException(val)) { | if (JS_IsException(val)) { | ||||
setMessage(ErrorToString(ctx)); | |||||
display(ErrorToString(ctx)); | |||||
JS_FreeValue(ctx, val); | JS_FreeValue(ctx, val); | ||||
JS_FreeValue(ctx, global_obj); | JS_FreeValue(ctx, global_obj); | ||||
return -1; | return -1; | ||||
} | } | ||||
ProcessBlock* block = getProcessBlock(); | |||||
// config: Read values | // config: Read values | ||||
config = JS_GetPropertyStr(ctx, global_obj, "config"); | config = JS_GetPropertyStr(ctx, global_obj, "config"); | ||||
{ | { | ||||
@@ -128,7 +129,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
JSValue buffer = JS_GetPropertyStr(ctx, config, "bufferSize"); | JSValue buffer = JS_GetPropertyStr(ctx, config, "bufferSize"); | ||||
int32_t bufferValue; | int32_t bufferValue; | ||||
if (JS_ToInt32(ctx, &bufferValue, buffer) == 0) { | if (JS_ToInt32(ctx, &bufferValue, buffer) == 0) { | ||||
block->bufferSize = bufferValue; | |||||
setBufferSize(bufferValue); | |||||
} | } | ||||
JS_FreeValue(ctx, config); | JS_FreeValue(ctx, config); | ||||
@@ -204,7 +205,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
if (JS_IsException(hack)) { | if (JS_IsException(hack)) { | ||||
std::string errorString = ErrorToString(ctx); | std::string errorString = ErrorToString(ctx); | ||||
WARN("QuickJS: %s", errorString.c_str()); | WARN("QuickJS: %s", errorString.c_str()); | ||||
setMessage(errorString.c_str()); | |||||
display(errorString.c_str()); | |||||
} | } | ||||
JS_FreeValue(ctx, hack); | JS_FreeValue(ctx, hack); | ||||
@@ -215,7 +216,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
if (JS_IsException(val)) { | if (JS_IsException(val)) { | ||||
std::string errorString = ErrorToString(ctx); | std::string errorString = ErrorToString(ctx); | ||||
WARN("QuickJS: %s", errorString.c_str()); | WARN("QuickJS: %s", errorString.c_str()); | ||||
setMessage(errorString.c_str()); | |||||
display(errorString.c_str()); | |||||
JS_FreeValue(ctx, val); | JS_FreeValue(ctx, val); | ||||
JS_FreeValue(ctx, blockIdx); | JS_FreeValue(ctx, blockIdx); | ||||
@@ -232,6 +233,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
JSValue global_obj = JS_GetGlobalObject(ctx); | JSValue global_obj = JS_GetGlobalObject(ctx); | ||||
// block | // block | ||||
ProcessBlock* block = getProcessBlock(); | |||||
JSValue blockIdx = JS_GetPropertyStr(ctx, global_obj, "block"); | JSValue blockIdx = JS_GetPropertyStr(ctx, global_obj, "block"); | ||||
{ | { | ||||
// sampleRate | // sampleRate | ||||
@@ -254,7 +256,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
if (JS_IsException(val)) { | if (JS_IsException(val)) { | ||||
std::string errorString = ErrorToString(ctx); | std::string errorString = ErrorToString(ctx); | ||||
WARN("QuickJS: %s", errorString.c_str()); | WARN("QuickJS: %s", errorString.c_str()); | ||||
setMessage(errorString.c_str()); | |||||
display(errorString.c_str()); | |||||
JS_FreeValue(ctx, val); | JS_FreeValue(ctx, val); | ||||
JS_FreeValue(ctx, process); | JS_FreeValue(ctx, process); | ||||
@@ -286,7 +288,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
int argc, JSValueConst *argv) { | int argc, JSValueConst *argv) { | ||||
if (argc) { | if (argc) { | ||||
const char *s = JS_ToCString(ctx, argv[0]); | const char *s = JS_ToCString(ctx, argv[0]); | ||||
INFO("VCV Prototype: %s", s); | |||||
INFO("QuickJS: %s", s); | |||||
} | } | ||||
return JS_UNDEFINED; | return JS_UNDEFINED; | ||||
} | } | ||||
@@ -294,7 +296,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
int argc, JSValueConst *argv) { | int argc, JSValueConst *argv) { | ||||
if (argc) { | if (argc) { | ||||
const char *s = JS_ToCString(ctx, argv[0]); | const char *s = JS_ToCString(ctx, argv[0]); | ||||
DEBUG("VCV Prototype: %s", s); | |||||
DEBUG("QuickJS: %s", s); | |||||
} | } | ||||
return JS_UNDEFINED; | return JS_UNDEFINED; | ||||
} | } | ||||
@@ -302,7 +304,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
int argc, JSValueConst *argv) { | int argc, JSValueConst *argv) { | ||||
if (argc) { | if (argc) { | ||||
const char *s = JS_ToCString(ctx, argv[0]); | const char *s = JS_ToCString(ctx, argv[0]); | ||||
INFO("VCV Prototype: %s", s); | |||||
INFO("QuickJS: %s", s); | |||||
} | } | ||||
return JS_UNDEFINED; | return JS_UNDEFINED; | ||||
} | } | ||||
@@ -310,7 +312,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
int argc, JSValueConst *argv) { | int argc, JSValueConst *argv) { | ||||
if (argc) { | if (argc) { | ||||
const char *s = JS_ToCString(ctx, argv[0]); | const char *s = JS_ToCString(ctx, argv[0]); | ||||
WARN("VCV Prototype: %s", s); | |||||
WARN("QuickJS: %s", s); | |||||
} | } | ||||
return JS_UNDEFINED; | return JS_UNDEFINED; | ||||
} | } | ||||
@@ -318,7 +320,7 @@ struct QuickJSEngine : ScriptEngine { | |||||
int argc, JSValueConst *argv) { | int argc, JSValueConst *argv) { | ||||
if (argc) { | if (argc) { | ||||
const char *s = JS_ToCString(ctx, argv[0]); | const char *s = JS_ToCString(ctx, argv[0]); | ||||
getQuickJSEngine(ctx)->setMessage(s); | |||||
getQuickJSEngine(ctx)->display(s); | |||||
} | } | ||||
return JS_UNDEFINED; | return JS_UNDEFINED; | ||||
} | } | ||||
@@ -9,21 +9,20 @@ static const int MAX_BUFFER_SIZE = 4096; | |||||
struct Prototype; | struct Prototype; | ||||
struct ScriptEngine { | |||||
struct ProcessBlock { | |||||
float sampleRate = 0.f; | |||||
float sampleTime = 0.f; | |||||
int bufferSize = 1; | |||||
float inputs[NUM_ROWS][MAX_BUFFER_SIZE] = {}; | |||||
float outputs[NUM_ROWS][MAX_BUFFER_SIZE] = {}; | |||||
float knobs[NUM_ROWS] = {}; | |||||
bool switches[NUM_ROWS] = {}; | |||||
float lights[NUM_ROWS][3] = {}; | |||||
float switchLights[NUM_ROWS][3] = {}; | |||||
}; | |||||
/** Set by module */ | |||||
ProcessBlock* block = NULL; | |||||
struct ProcessBlock { | |||||
float sampleRate = 0.f; | |||||
float sampleTime = 0.f; | |||||
int bufferSize = 1; | |||||
float inputs[NUM_ROWS][MAX_BUFFER_SIZE] = {}; | |||||
float outputs[NUM_ROWS][MAX_BUFFER_SIZE] = {}; | |||||
float knobs[NUM_ROWS] = {}; | |||||
bool switches[NUM_ROWS] = {}; | |||||
float lights[NUM_ROWS][3] = {}; | |||||
float switchLights[NUM_ROWS][3] = {}; | |||||
}; | |||||
struct ScriptEngine { | |||||
// Virtual methods for subclasses | // Virtual methods for subclasses | ||||
virtual ~ScriptEngine() {} | virtual ~ScriptEngine() {} | ||||
virtual std::string getEngineName() {return "";} | virtual std::string getEngineName() {return "";} | ||||
@@ -40,8 +39,10 @@ struct ScriptEngine { | |||||
// Communication with Prototype module. | // Communication with Prototype module. | ||||
// These cannot be called from your constructor, so initialize your engine in the run() method. | // These cannot be called from your constructor, so initialize your engine in the run() method. | ||||
void setMessage(const std::string& message); | |||||
void display(const std::string& message); | |||||
void setFrameDivider(int frameDivider); | void setFrameDivider(int frameDivider); | ||||
void setBufferSize(int bufferSize); | |||||
ProcessBlock* getProcessBlock(); | |||||
// private | // private | ||||
Prototype* module = NULL; | Prototype* module = NULL; | ||||
}; | }; | ||||
@@ -53,11 +54,14 @@ struct ScriptEngine { | |||||
ScriptEngine* createDuktapeEngine(); | ScriptEngine* createDuktapeEngine(); | ||||
ScriptEngine* createQuickJSEngine(); | ScriptEngine* createQuickJSEngine(); | ||||
ScriptEngine* createPythonEngine(); | ScriptEngine* createPythonEngine(); | ||||
ScriptEngine* createLuaJITEngine(); | |||||
inline ScriptEngine* createScriptEngine(std::string ext) { | inline ScriptEngine* createScriptEngine(std::string ext) { | ||||
ext = rack::string::lowercase(ext); | ext = rack::string::lowercase(ext); | ||||
if (ext == "js") | if (ext == "js") | ||||
return createQuickJSEngine(); | return createQuickJSEngine(); | ||||
else if (ext == "lua") | |||||
return createLuaJITEngine(); | |||||
else if (ext == "py") | else if (ext == "py") | ||||
return createPythonEngine(); | return createPythonEngine(); | ||||
// Add your file extension check here. | // Add your file extension check here. | ||||
@@ -0,0 +1,11 @@ | |||||
config.frameDivider = 1 | |||||
config.bufferSize = 16 | |||||
function process(block) | |||||
for j=1,block.bufferSize do | |||||
block.outputs[1][j] = math.random() | |||||
end | |||||
end | |||||
print("Hello, world!") |