@@ -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!") |