| @@ -16,6 +16,7 @@ include $(RACK_DIR)/arch.mk | |||
| DUKTAPE ?= 0 | |||
| QUICKJS ?= 1 | |||
| PYTHON ?= 1 | |||
| LUA ?= 1 | |||
| # Entropia File System Watcher | |||
| 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 | |||
| 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 := dep/lib/libjulia.a | |||
| @@ -124,6 +124,6 @@ sudo pacman -S premake | |||
| ## Contributors | |||
| - [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) | |||
| - add your name here | |||
| @@ -3,13 +3,13 @@ | |||
| function process(block) { | |||
| // Loop through each column | |||
| for (var i = 0; i < 6; i++) { | |||
| for (let i = 0; i < 6; i++) { | |||
| // Get input | |||
| var x = block.inputs[i][0] | |||
| let x = block.inputs[i][0] | |||
| // Get gain knob | |||
| var gain = block.knobs[i] | |||
| let gain = block.knobs[i] | |||
| // Apply gain to input | |||
| var y = x * gain | |||
| let y = x * gain | |||
| // Set gain light (red = 0) | |||
| block.lights[i][0] = gain | |||
| // 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 { | |||
| assert(!ctx); | |||
| ProcessBlock* block = getProcessBlock(); | |||
| // Create duktape context | |||
| ctx = duk_create_heap_default(); | |||
| if (!ctx) { | |||
| setMessage("Could not create duktape context"); | |||
| display("Could not create duktape context"); | |||
| return -1; | |||
| } | |||
| // Initialize globals | |||
| // user pointer | |||
| duk_push_pointer(ctx, this); | |||
| duk_put_global_string(ctx, DUK_HIDDEN_SYMBOL("p")); | |||
| duk_put_global_string(ctx, DUK_HIDDEN_SYMBOL("engine")); | |||
| // console | |||
| duk_idx_t consoleIdx = duk_push_object(ctx); | |||
| @@ -34,15 +36,6 @@ struct DuktapeEngine : ScriptEngine { | |||
| // log | |||
| duk_push_c_function(ctx, native_console_log, 1); | |||
| 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"); | |||
| @@ -67,7 +60,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| if (duk_pcompile_lstring_filename(ctx, 0, script.c_str(), script.size()) != 0) { | |||
| const char* s = duk_safe_to_string(ctx, -1); | |||
| WARN("duktape: %s", s); | |||
| setMessage(s); | |||
| display(s); | |||
| duk_pop(ctx); | |||
| return -1; | |||
| } | |||
| @@ -75,7 +68,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| if (duk_pcall(ctx, 0)) { | |||
| const char* s = duk_safe_to_string(ctx, -1); | |||
| WARN("duktape: %s", s); | |||
| setMessage(s); | |||
| display(s); | |||
| duk_pop(ctx); | |||
| return -1; | |||
| } | |||
| @@ -91,7 +84,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| duk_pop(ctx); | |||
| // 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); | |||
| @@ -99,7 +92,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| // Keep process function on stack for faster calling | |||
| duk_get_global_string(ctx, "process"); | |||
| if (!duk_is_function(ctx, -1)) { | |||
| setMessage("No process() function"); | |||
| display("No process() function"); | |||
| return -1; | |||
| } | |||
| @@ -170,6 +163,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| int process() override { | |||
| // block | |||
| ProcessBlock* block = getProcessBlock(); | |||
| duk_idx_t blockIdx = duk_get_top(ctx) - 1; | |||
| { | |||
| // sampleRate | |||
| @@ -193,7 +187,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| if (duk_pcall(ctx, 1)) { | |||
| const char* s = duk_safe_to_string(ctx, -1); | |||
| WARN("duktape: %s", s); | |||
| setMessage(s); | |||
| display(s); | |||
| duk_pop(ctx); | |||
| return -1; | |||
| } | |||
| @@ -204,7 +198,7 @@ struct DuktapeEngine : ScriptEngine { | |||
| } | |||
| 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); | |||
| duk_pop(ctx); | |||
| return engine; | |||
| @@ -212,22 +206,12 @@ struct DuktapeEngine : ScriptEngine { | |||
| static duk_ret_t native_console_log(duk_context* ctx) { | |||
| 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; | |||
| } | |||
| static duk_ret_t native_display(duk_context* ctx) { | |||
| const char* s = duk_safe_to_string(ctx, -1); | |||
| getDuktapeEngine(ctx)->setMessage(s); | |||
| getDuktapeEngine(ctx)->display(s); | |||
| 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; | |||
| int frame = 0; | |||
| int frameDivider; | |||
| ScriptEngine::ProcessBlock* block; | |||
| ProcessBlock* block; | |||
| int bufferIndex = 0; | |||
| efsw_watcher efsw = NULL; | |||
| @@ -53,7 +53,7 @@ struct Prototype : Module { | |||
| for (int i = 0; i < NUM_ROWS; i++) | |||
| configParam(SWITCH_PARAMS + i, 0.f, 1.f, 0.f, string::f("Switch %d", i + 1)); | |||
| block = new ScriptEngine::ProcessBlock; | |||
| block = new ProcessBlock; | |||
| setPath(""); | |||
| } | |||
| @@ -179,7 +179,7 @@ struct Prototype : Module { | |||
| // Reset process state | |||
| frameDivider = 32; | |||
| frame = 0; | |||
| *block = ScriptEngine::ProcessBlock(); | |||
| *block = ProcessBlock(); | |||
| bufferIndex = 0; | |||
| // Reset outputs and lights because they might hold old values | |||
| for (int i = 0; i < NUM_ROWS; i++) | |||
| @@ -203,7 +203,6 @@ struct Prototype : Module { | |||
| return; | |||
| } | |||
| scriptEngine->module = this; | |||
| scriptEngine->block = block; | |||
| // Run script | |||
| if (scriptEngine->run(path, script)) { | |||
| @@ -212,7 +211,6 @@ struct Prototype : Module { | |||
| scriptEngine = NULL; | |||
| return; | |||
| } | |||
| block->bufferSize = clamp(block->bufferSize, 1, MAX_BUFFER_SIZE); | |||
| 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; | |||
| } | |||
| void ScriptEngine::setFrameDivider(int 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 { | |||
| @@ -103,6 +103,7 @@ struct PythonEngine : ScriptEngine { | |||
| DEFER({Py_DECREF(result);}); | |||
| // Create block | |||
| ProcessBlock* block = getProcessBlock(); | |||
| static PyStructSequence_Field blockFields[] = { | |||
| {"inputs", ""}, | |||
| {"outputs", ""}, | |||
| @@ -154,11 +155,11 @@ struct PythonEngine : ScriptEngine { | |||
| // Get process function from globals | |||
| processFunc = PyDict_GetItemString(mainDict, "process"); | |||
| if (!processFunc) { | |||
| setMessage("No process() function"); | |||
| display("No process() function"); | |||
| return -1; | |||
| } | |||
| if (!PyCallable_Check(processFunc)) { | |||
| setMessage("process() is not callable"); | |||
| display("process() is not callable"); | |||
| return -1; | |||
| } | |||
| @@ -181,7 +182,7 @@ struct PythonEngine : ScriptEngine { | |||
| // if (!str) | |||
| // return -1; | |||
| // setMessage(str); | |||
| // display(str); | |||
| return -1; | |||
| } | |||
| DEFER({Py_DECREF(processResult);}); | |||
| @@ -209,7 +210,7 @@ struct PythonEngine : ScriptEngine { | |||
| DEFER({Py_DECREF(msgS);}); | |||
| const char* msg = PyUnicode_AsUTF8(msgS); | |||
| that->setMessage(msg); | |||
| that->display(msg); | |||
| Py_INCREF(Py_None); | |||
| return Py_None; | |||
| @@ -65,7 +65,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| // Create quickjs context | |||
| ctx = JS_NewContext(rt); | |||
| if (!ctx) { | |||
| setMessage("Could not create QuickJS context"); | |||
| display("Could not create QuickJS context"); | |||
| return -1; | |||
| } | |||
| @@ -107,13 +107,14 @@ struct QuickJSEngine : ScriptEngine { | |||
| // Compile string | |||
| JSValue val = JS_Eval(ctx, script.c_str(), script.size(), path.c_str(), 0); | |||
| if (JS_IsException(val)) { | |||
| setMessage(ErrorToString(ctx)); | |||
| display(ErrorToString(ctx)); | |||
| JS_FreeValue(ctx, val); | |||
| JS_FreeValue(ctx, global_obj); | |||
| return -1; | |||
| } | |||
| ProcessBlock* block = getProcessBlock(); | |||
| // config: Read values | |||
| config = JS_GetPropertyStr(ctx, global_obj, "config"); | |||
| { | |||
| @@ -128,7 +129,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| JSValue buffer = JS_GetPropertyStr(ctx, config, "bufferSize"); | |||
| int32_t bufferValue; | |||
| if (JS_ToInt32(ctx, &bufferValue, buffer) == 0) { | |||
| block->bufferSize = bufferValue; | |||
| setBufferSize(bufferValue); | |||
| } | |||
| JS_FreeValue(ctx, config); | |||
| @@ -204,7 +205,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| if (JS_IsException(hack)) { | |||
| std::string errorString = ErrorToString(ctx); | |||
| WARN("QuickJS: %s", errorString.c_str()); | |||
| setMessage(errorString.c_str()); | |||
| display(errorString.c_str()); | |||
| } | |||
| JS_FreeValue(ctx, hack); | |||
| @@ -215,7 +216,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| if (JS_IsException(val)) { | |||
| std::string errorString = ErrorToString(ctx); | |||
| WARN("QuickJS: %s", errorString.c_str()); | |||
| setMessage(errorString.c_str()); | |||
| display(errorString.c_str()); | |||
| JS_FreeValue(ctx, val); | |||
| JS_FreeValue(ctx, blockIdx); | |||
| @@ -232,6 +233,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| JSValue global_obj = JS_GetGlobalObject(ctx); | |||
| // block | |||
| ProcessBlock* block = getProcessBlock(); | |||
| JSValue blockIdx = JS_GetPropertyStr(ctx, global_obj, "block"); | |||
| { | |||
| // sampleRate | |||
| @@ -254,7 +256,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| if (JS_IsException(val)) { | |||
| std::string errorString = ErrorToString(ctx); | |||
| WARN("QuickJS: %s", errorString.c_str()); | |||
| setMessage(errorString.c_str()); | |||
| display(errorString.c_str()); | |||
| JS_FreeValue(ctx, val); | |||
| JS_FreeValue(ctx, process); | |||
| @@ -286,7 +288,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| int argc, JSValueConst *argv) { | |||
| if (argc) { | |||
| const char *s = JS_ToCString(ctx, argv[0]); | |||
| INFO("VCV Prototype: %s", s); | |||
| INFO("QuickJS: %s", s); | |||
| } | |||
| return JS_UNDEFINED; | |||
| } | |||
| @@ -294,7 +296,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| int argc, JSValueConst *argv) { | |||
| if (argc) { | |||
| const char *s = JS_ToCString(ctx, argv[0]); | |||
| DEBUG("VCV Prototype: %s", s); | |||
| DEBUG("QuickJS: %s", s); | |||
| } | |||
| return JS_UNDEFINED; | |||
| } | |||
| @@ -302,7 +304,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| int argc, JSValueConst *argv) { | |||
| if (argc) { | |||
| const char *s = JS_ToCString(ctx, argv[0]); | |||
| INFO("VCV Prototype: %s", s); | |||
| INFO("QuickJS: %s", s); | |||
| } | |||
| return JS_UNDEFINED; | |||
| } | |||
| @@ -310,7 +312,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| int argc, JSValueConst *argv) { | |||
| if (argc) { | |||
| const char *s = JS_ToCString(ctx, argv[0]); | |||
| WARN("VCV Prototype: %s", s); | |||
| WARN("QuickJS: %s", s); | |||
| } | |||
| return JS_UNDEFINED; | |||
| } | |||
| @@ -318,7 +320,7 @@ struct QuickJSEngine : ScriptEngine { | |||
| int argc, JSValueConst *argv) { | |||
| if (argc) { | |||
| const char *s = JS_ToCString(ctx, argv[0]); | |||
| getQuickJSEngine(ctx)->setMessage(s); | |||
| getQuickJSEngine(ctx)->display(s); | |||
| } | |||
| return JS_UNDEFINED; | |||
| } | |||
| @@ -9,21 +9,20 @@ static const int MAX_BUFFER_SIZE = 4096; | |||
| 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 ~ScriptEngine() {} | |||
| virtual std::string getEngineName() {return "";} | |||
| @@ -40,8 +39,10 @@ struct ScriptEngine { | |||
| // Communication with Prototype module. | |||
| // 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 setBufferSize(int bufferSize); | |||
| ProcessBlock* getProcessBlock(); | |||
| // private | |||
| Prototype* module = NULL; | |||
| }; | |||
| @@ -53,11 +54,14 @@ struct ScriptEngine { | |||
| ScriptEngine* createDuktapeEngine(); | |||
| ScriptEngine* createQuickJSEngine(); | |||
| ScriptEngine* createPythonEngine(); | |||
| ScriptEngine* createLuaJITEngine(); | |||
| inline ScriptEngine* createScriptEngine(std::string ext) { | |||
| ext = rack::string::lowercase(ext); | |||
| if (ext == "js") | |||
| return createQuickJSEngine(); | |||
| else if (ext == "lua") | |||
| return createLuaJITEngine(); | |||
| else if (ext == "py") | |||
| return createPythonEngine(); | |||
| // 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!") | |||