@@ -7,52 +7,73 @@ Scripting language host for [VCV Rack](https://vcvrack.com/) containing: | |||||
- 6 lights (RGB LEDs) | - 6 lights (RGB LEDs) | ||||
- 6 switches with RGB LEDs | - 6 switches with RGB LEDs | ||||
[Discussion thread](https://community.vcvrack.com/t/vcv-prototype/3271/1) | |||||
### Scripting API | ### Scripting API | ||||
This is a reference API for the `DuktapeEngine` (JavaScript). | |||||
Other script engines may vary in their syntax (e.g. `block.inputs[i][j]` vs `block.getInput(i, j)` vs `input(i, j)`). | |||||
This is the reference API for the JavaScript script engine, along with default property values. | |||||
Other script engines may vary in their syntax (e.g. `args.inputs[i][j]` vs `args.getInput(i, j)` vs `input(i, j)`), but the functionality should be similar. | |||||
```js | ```js | ||||
// Display message on LED display. | |||||
/** Display message on LED display. | |||||
*/ | |||||
display(message) | display(message) | ||||
// Skip this many sample frames before running process(). | |||||
// For sequencers, 32 is reasonable since process() will be called every 0.7ms with a 44100kHz sample rate. | |||||
// For audio generators and processors, 1 is recommended. If this is too slow for your purposes, write a C++ plugin. | |||||
config.frameDivider = 1 | |||||
// Number of samples to store each block passed to process(). | |||||
// Latency introduced by buffers is `bufferSize * frameDivider * sampleTime`. | |||||
config.bufferSize = 1 | |||||
// Called when the next block is ready to be processed. | |||||
function process(block) { | |||||
// Engine sample rate in Hz. Read-only. | |||||
block.sampleRate | |||||
// Equal to `1 / sampleRate`. Read-only. | |||||
block.sampleTime | |||||
// The actual buffer size, requested by `config.bufferSize`. Read-only. | |||||
block.bufferSize | |||||
// Voltage of the input port of row `i` and buffer index `j`. Read-only. | |||||
block.inputs[i][j] | |||||
// Voltage of the output port of row `i` and buffer index `j`. Writable. | |||||
block.outputs[i][j] | |||||
// Value of the knob of row `i`. Between 0 and 1. Read-only. | |||||
block.knobs[i] | |||||
// Pressed state of the switch of row `i`. Read-only. | |||||
block.switches[i] | |||||
// Brightness of the RGB LED of row `i` and color index `c`. Writable. | |||||
// `c=0` for red, `c=1` for green, `c=2` for blue. | |||||
block.lights[i][c] | |||||
// Brightness of the switch RGB LED of row `i` and color index `c`. Writable. | |||||
block.switchLights[i][c] | |||||
/** Skip this many sample frames before running process(). | |||||
For CV generators and processors, 256 is reasonable. | |||||
For sequencers, 32 is reasonable since process() will be called every 0.7ms with a 44100kHz sample rate, which will capture 1ms-long triggers. | |||||
For audio generators and processors, 1-8 is recommended, but it will consume lots of CPU. | |||||
If this is too slow for your purposes, you should just write a C++ plugin. | |||||
*/ | |||||
config.frameDivider // 32 | |||||
/** Called when the next args is ready to be processed. | |||||
*/ | |||||
function process(args) { | |||||
/** Engine sample rate in Hz. Read-only. | |||||
*/ | |||||
args.sampleRate | |||||
/** Engine sample timestep in seconds. Equal to `1 / sampleRate`. Read-only. | |||||
*/ | |||||
args.sampleTime | |||||
/** Voltage of the input port of row `i`. Read-only. | |||||
*/ | |||||
args.inputs[i] // 0.0 | |||||
/** Voltage of the output port of row `i`. Writable. | |||||
*/ | |||||
args.outputs[i] // 0.0 | |||||
/** Value of the knob of row `i`. Between 0 and 1. Read-only. | |||||
*/ | |||||
args.knobs[i] // 0.0 | |||||
/** Pressed state of the switch of row `i`. Read-only. | |||||
*/ | |||||
args.switches[i] // false | |||||
/** Brightness of the RGB LED of row `i`. Writable. | |||||
*/ | |||||
args.lights[i].r // 0.0 | |||||
args.lights[i].g // 0.0 | |||||
args.lights[i].b // 0.0 | |||||
/** Brightness of the switch RGB LED of row `i`. Writable. | |||||
*/ | |||||
args.switchLights[i].r // 0.0 | |||||
args.switchLights[i].g // 0.0 | |||||
args.switchLights[i].b // 0.0 | |||||
} | } | ||||
``` | ``` | ||||
### Adding a script engine | ### Adding a script engine | ||||
- Add your scripting language library to the build system so it builds with `make dep`, following the Duktape example in the `Makefile`. | - Add your scripting language library to the build system so it builds with `make dep`, following the Duktape example in the `Makefile`. | ||||
- Create a `MyEngine.cpp` file in `src/` with a `ScriptEngine` subclass defining the virtual methods, following `src/DuktapeEngine.cpp` as an example. | |||||
- Create a `MyEngine.cpp` file (for example) in `src/` with a `ScriptEngine` subclass defining the virtual methods, following `src/DuktapeEngine.cpp` as an example. | |||||
- Add your engine to the "List of ScriptEngines" in `src/ScriptEngine.cpp`. | - Add your engine to the "List of ScriptEngines" in `src/ScriptEngine.cpp`. | ||||
- Build and test VCV Prototype. | |||||
- Build and test the plugin. | |||||
- Add a few example scripts and tests to `examples/`. These will be included in the plugin package for the user. | - Add a few example scripts and tests to `examples/`. These will be included in the plugin package for the user. | ||||
- Add your name to the Contributers list below. | - Add your name to the Contributers list below. | ||||
- Send a pull request. Once merged, you will be added as a repo maintainer. Be sure to "watch" this repo to be notified of bugs in your engine. | - Send a pull request. Once merged, you will be added as a repo maintainer. Be sure to "watch" this repo to be notified of bugs in your engine. | ||||
@@ -1,5 +1,5 @@ | |||||
// Call process() every 128 audio samples | |||||
config.frameDivider = 128 | |||||
// Call process() every 256 audio samples | |||||
config.frameDivider = 256 | |||||
// From https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB | // From https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB | ||||
@@ -25,14 +25,12 @@ function process(args) { | |||||
phase %= 1 | phase %= 1 | ||||
for (var i = 0; i < 6; i++) { | for (var i = 0; i < 6; i++) { | ||||
var h = (i / 6 + phase) % 1 | |||||
var h = (1 - i / 6 + phase) % 1 | |||||
var rgb = hsvToRgb(h, 1, 1) | var rgb = hsvToRgb(h, 1, 1) | ||||
args.lights[i] = rgb | args.lights[i] = rgb | ||||
args.switchLights[i] = rgb | args.switchLights[i] = rgb | ||||
args.outputs[i] = Math.sin(2 * Math.PI * h) * 10 | args.outputs[i] = Math.sin(2 * Math.PI * h) * 10 | ||||
} | } | ||||
display(phase) | |||||
} | } | ||||
display("Hello, world!") | display("Hello, world!") |
@@ -94,8 +94,8 @@ struct DuktapeEngine : ScriptEngine { | |||||
return -1; | return -1; | ||||
} | } | ||||
// block (keep on stack) | |||||
duk_idx_t blockIdx = duk_push_object(ctx); | |||||
// args (keep on stack) | |||||
duk_idx_t argsIdx = duk_push_object(ctx); | |||||
{ | { | ||||
// inputs | // inputs | ||||
duk_idx_t inputsIdx = duk_push_array(ctx); | duk_idx_t inputsIdx = duk_push_array(ctx); | ||||
@@ -103,7 +103,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_push_number(ctx, 0.0); | duk_push_number(ctx, 0.0); | ||||
duk_put_prop_index(ctx, inputsIdx, i); | duk_put_prop_index(ctx, inputsIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "inputs"); | |||||
duk_put_prop_string(ctx, argsIdx, "inputs"); | |||||
// outputs | // outputs | ||||
duk_idx_t outputsIdx = duk_push_array(ctx); | duk_idx_t outputsIdx = duk_push_array(ctx); | ||||
@@ -111,7 +111,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_push_number(ctx, 0.0); | duk_push_number(ctx, 0.0); | ||||
duk_put_prop_index(ctx, outputsIdx, i); | duk_put_prop_index(ctx, outputsIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "outputs"); | |||||
duk_put_prop_string(ctx, argsIdx, "outputs"); | |||||
// knobs | // knobs | ||||
duk_idx_t knobsIdx = duk_push_array(ctx); | duk_idx_t knobsIdx = duk_push_array(ctx); | ||||
@@ -119,7 +119,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_push_number(ctx, 0.0); | duk_push_number(ctx, 0.0); | ||||
duk_put_prop_index(ctx, knobsIdx, i); | duk_put_prop_index(ctx, knobsIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "knobs"); | |||||
duk_put_prop_string(ctx, argsIdx, "knobs"); | |||||
// switches | // switches | ||||
duk_idx_t switchesIdx = duk_push_array(ctx); | duk_idx_t switchesIdx = duk_push_array(ctx); | ||||
@@ -127,7 +127,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_push_number(ctx, 0.0); | duk_push_number(ctx, 0.0); | ||||
duk_put_prop_index(ctx, switchesIdx, i); | duk_put_prop_index(ctx, switchesIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "switches"); | |||||
duk_put_prop_string(ctx, argsIdx, "switches"); | |||||
// lights | // lights | ||||
duk_idx_t lightsIdx = duk_push_array(ctx); | duk_idx_t lightsIdx = duk_push_array(ctx); | ||||
@@ -143,7 +143,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
} | } | ||||
duk_put_prop_index(ctx, lightsIdx, i); | duk_put_prop_index(ctx, lightsIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "lights"); | |||||
duk_put_prop_string(ctx, argsIdx, "lights"); | |||||
// switchLights | // switchLights | ||||
duk_idx_t switchLightsIdx = duk_push_array(ctx); | duk_idx_t switchLightsIdx = duk_push_array(ctx); | ||||
@@ -159,44 +159,44 @@ struct DuktapeEngine : ScriptEngine { | |||||
} | } | ||||
duk_put_prop_index(ctx, switchLightsIdx, i); | duk_put_prop_index(ctx, switchLightsIdx, i); | ||||
} | } | ||||
duk_put_prop_string(ctx, blockIdx, "switchLights"); | |||||
duk_put_prop_string(ctx, argsIdx, "switchLights"); | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int process(ProcessBlock& block) override { | |||||
// block | |||||
duk_idx_t blockIdx = duk_get_top(ctx) - 1; | |||||
int process(ProcessArgs& args) override { | |||||
// args | |||||
duk_idx_t argsIdx = duk_get_top(ctx) - 1; | |||||
{ | { | ||||
// sampleRate | // sampleRate | ||||
duk_push_number(ctx, block.sampleRate); | |||||
duk_put_prop_string(ctx, blockIdx, "sampleRate"); | |||||
duk_push_number(ctx, args.sampleRate); | |||||
duk_put_prop_string(ctx, argsIdx, "sampleRate"); | |||||
// sampleTime | // sampleTime | ||||
duk_push_number(ctx, block.sampleTime); | |||||
duk_put_prop_string(ctx, blockIdx, "sampleTime"); | |||||
duk_push_number(ctx, args.sampleTime); | |||||
duk_put_prop_string(ctx, argsIdx, "sampleTime"); | |||||
// inputs | // inputs | ||||
duk_get_prop_string(ctx, blockIdx, "inputs"); | |||||
duk_get_prop_string(ctx, argsIdx, "inputs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | for (int i = 0; i < NUM_ROWS; i++) { | ||||
duk_push_number(ctx, block.inputs[i]); | |||||
duk_push_number(ctx, getInput(i)); | |||||
duk_put_prop_index(ctx, -2, i); | duk_put_prop_index(ctx, -2, i); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
// knobs | // knobs | ||||
duk_get_prop_string(ctx, blockIdx, "knobs"); | |||||
duk_get_prop_string(ctx, argsIdx, "knobs"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | for (int i = 0; i < NUM_ROWS; i++) { | ||||
duk_push_number(ctx, block.knobs[i]); | |||||
duk_push_number(ctx, getKnob(i)); | |||||
duk_put_prop_index(ctx, -2, i); | duk_put_prop_index(ctx, -2, i); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
// switches | // switches | ||||
duk_get_prop_string(ctx, blockIdx, "switches"); | |||||
duk_get_prop_string(ctx, argsIdx, "switches"); | |||||
for (int i = 0; i < NUM_ROWS; i++) { | for (int i = 0; i < NUM_ROWS; i++) { | ||||
duk_push_boolean(ctx, block.switches[i]); | |||||
duk_push_boolean(ctx, getSwitch(i)); | |||||
duk_put_prop_index(ctx, -2, i); | duk_put_prop_index(ctx, -2, i); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
@@ -204,7 +204,7 @@ struct DuktapeEngine : ScriptEngine { | |||||
// Duplicate process function | // Duplicate process function | ||||
duk_dup(ctx, -2); | duk_dup(ctx, -2); | ||||
// Duplicate block object | |||||
// Duplicate args object | |||||
duk_dup(ctx, -2); | duk_dup(ctx, -2); | ||||
// Call process function | // Call process function | ||||
if (duk_pcall(ctx, 1)) { | if (duk_pcall(ctx, 1)) { | ||||
@@ -216,13 +216,13 @@ struct DuktapeEngine : ScriptEngine { | |||||
// return value | // return value | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
// block | |||||
// args | |||||
{ | { | ||||
// outputs | // outputs | ||||
duk_get_prop_string(ctx, -1, "outputs"); | duk_get_prop_string(ctx, -1, "outputs"); | ||||
for (int i = 0; i < NUM_ROWS; i++) { | for (int i = 0; i < NUM_ROWS; i++) { | ||||
duk_get_prop_index(ctx, -1, i); | duk_get_prop_index(ctx, -1, i); | ||||
block.outputs[i] = duk_get_number(ctx, -1); | |||||
setOutput(i, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
@@ -233,13 +233,13 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_get_prop_index(ctx, -1, i); | duk_get_prop_index(ctx, -1, i); | ||||
{ | { | ||||
duk_get_prop_string(ctx, -1, "r"); | duk_get_prop_string(ctx, -1, "r"); | ||||
block.lights[i][0] = duk_get_number(ctx, -1); | |||||
setLight(i, 0, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
duk_get_prop_string(ctx, -1, "g"); | duk_get_prop_string(ctx, -1, "g"); | ||||
block.lights[i][1] = duk_get_number(ctx, -1); | |||||
setLight(i, 1, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
duk_get_prop_string(ctx, -1, "b"); | duk_get_prop_string(ctx, -1, "b"); | ||||
block.lights[i][2] = duk_get_number(ctx, -1); | |||||
setLight(i, 2, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
@@ -252,13 +252,13 @@ struct DuktapeEngine : ScriptEngine { | |||||
duk_get_prop_index(ctx, -1, i); | duk_get_prop_index(ctx, -1, i); | ||||
{ | { | ||||
duk_get_prop_string(ctx, -1, "r"); | duk_get_prop_string(ctx, -1, "r"); | ||||
block.switchLights[i][0] = duk_get_number(ctx, -1); | |||||
setSwitchLight(i, 0, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
duk_get_prop_string(ctx, -1, "g"); | duk_get_prop_string(ctx, -1, "g"); | ||||
block.switchLights[i][1] = duk_get_number(ctx, -1); | |||||
setSwitchLight(i, 1, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
duk_get_prop_string(ctx, -1, "b"); | duk_get_prop_string(ctx, -1, "b"); | ||||
block.switchLights[i][2] = duk_get_number(ctx, -1); | |||||
setSwitchLight(i, 2, duk_get_number(ctx, -1)); | |||||
duk_pop(ctx); | duk_pop(ctx); | ||||
} | } | ||||
duk_pop(ctx); | duk_pop(ctx); | ||||
@@ -278,17 +278,17 @@ 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); | ||||
rack::INFO("Prototype: %s", s); | |||||
rack::INFO("VCV Prototype: %s", s); | |||||
return 0; | return 0; | ||||
} | } | ||||
static duk_ret_t native_console_debug(duk_context* ctx) { | static duk_ret_t native_console_debug(duk_context* ctx) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
rack::DEBUG("Prototype: %s", s); | |||||
rack::DEBUG("VCV Prototype: %s", s); | |||||
return 0; | return 0; | ||||
} | } | ||||
static duk_ret_t native_console_warn(duk_context* ctx) { | static duk_ret_t native_console_warn(duk_context* ctx) { | ||||
const char* s = duk_safe_to_string(ctx, -1); | const char* s = duk_safe_to_string(ctx, -1); | ||||
rack::WARN("Prototype: %s", s); | |||||
rack::WARN("VCV Prototype: %s", s); | |||||
return 0; | return 0; | ||||
} | } | ||||
static duk_ret_t native_display(duk_context* ctx) { | static duk_ret_t native_display(duk_context* ctx) { | ||||
@@ -31,15 +31,14 @@ struct Prototype : Module { | |||||
NUM_LIGHTS | NUM_LIGHTS | ||||
}; | }; | ||||
std::string message; | |||||
std::string path; | std::string path; | ||||
std::string script; | std::string script; | ||||
std::string engineName; | std::string engineName; | ||||
ScriptEngine* scriptEngine = NULL; | |||||
std::mutex scriptMutex; | std::mutex scriptMutex; | ||||
std::string message; | |||||
ScriptEngine* scriptEngine = NULL; | |||||
int frame = 0; | int frame = 0; | ||||
int frameDivider; | int frameDivider; | ||||
ScriptEngine::ProcessBlock block; | |||||
Prototype() { | Prototype() { | ||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
@@ -65,40 +64,20 @@ struct Prototype : Module { | |||||
return; | return; | ||||
frame = 0; | frame = 0; | ||||
// Inputs | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
block.inputs[i] = inputs[IN_INPUTS + i].getVoltage(); | |||||
// Params | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
block.knobs[i] = params[KNOB_PARAMS + i].getValue(); | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
block.switches[i] = (params[SWITCH_PARAMS + i].getValue() > 0.f); | |||||
// Set other block parameters | |||||
block.sampleRate = args.sampleRate; | |||||
block.sampleTime = args.sampleTime; | |||||
ScriptEngine::ProcessArgs scriptArgs; | |||||
scriptArgs.sampleRate = args.sampleRate; | |||||
scriptArgs.sampleTime = args.sampleTime; | |||||
{ | { | ||||
std::lock_guard<std::mutex> lock(scriptMutex); | std::lock_guard<std::mutex> lock(scriptMutex); | ||||
// Check for certain inside the mutex | // Check for certain inside the mutex | ||||
if (scriptEngine) { | if (scriptEngine) { | ||||
if (scriptEngine->process(block)) { | |||||
if (scriptEngine->process(scriptArgs)) { | |||||
clearScriptEngine(); | clearScriptEngine(); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Outputs | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
outputs[OUT_OUTPUTS + i].setVoltage(block.outputs[i]); | |||||
// Lights | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
for (int c = 0; c < 3; c++) | |||||
lights[LIGHT_LIGHTS + i * 3 + c].setBrightness(block.lights[i][c]); | |||||
for (int i = 0; i < NUM_ROWS; i++) | |||||
for (int c = 0; c < 3; c++) | |||||
lights[SWITCH_LIGHTS + i * 3 + c].setBrightness(block.switchLights[i][c]); | |||||
} | } | ||||
void clearScriptEngine() { | void clearScriptEngine() { | ||||
@@ -115,9 +94,9 @@ struct Prototype : Module { | |||||
for (int i = 0; i < NUM_ROWS; i++) | for (int i = 0; i < NUM_ROWS; i++) | ||||
for (int c = 0; c < 3; c++) | for (int c = 0; c < 3; c++) | ||||
lights[SWITCH_LIGHTS + i * 3 + c].setBrightness(0.f); | lights[SWITCH_LIGHTS + i * 3 + c].setBrightness(0.f); | ||||
std::memset(block.inputs, 0, sizeof(block.inputs)); | |||||
// Reset settings | // Reset settings | ||||
frameDivider = 32; | frameDivider = 32; | ||||
frame = 0; | |||||
} | } | ||||
void setScriptString(std::string path, std::string script) { | void setScriptString(std::string path, std::string script) { | ||||
@@ -131,6 +110,7 @@ struct Prototype : Module { | |||||
if (path == "") { | if (path == "") { | ||||
return; | return; | ||||
} | } | ||||
INFO("Loading script %s", path.c_str()); | |||||
std::string ext = string::filenameExtension(string::filename(path)); | std::string ext = string::filenameExtension(string::filename(path)); | ||||
scriptEngine = createScriptEngine(ext); | scriptEngine = createScriptEngine(ext); | ||||
if (!scriptEngine) { | if (!scriptEngine) { | ||||
@@ -164,6 +144,7 @@ struct Prototype : Module { | |||||
clearScriptEngine(); | clearScriptEngine(); | ||||
return; | return; | ||||
} | } | ||||
INFO("Successfully ran script %s", this->path.c_str()); | |||||
} | } | ||||
json_t* dataToJson() override { | json_t* dataToJson() override { | ||||
@@ -200,6 +181,24 @@ int ScriptEngine::getFrameDivider() { | |||||
void ScriptEngine::setFrameDivider(int frameDivider) { | void ScriptEngine::setFrameDivider(int frameDivider) { | ||||
module->frameDivider = frameDivider; | module->frameDivider = frameDivider; | ||||
} | } | ||||
float ScriptEngine::getInput(int index) { | |||||
return module->inputs[Prototype::IN_INPUTS + index].getVoltage(); | |||||
} | |||||
void ScriptEngine::setOutput(int index, float voltage) { | |||||
module->outputs[Prototype::OUT_OUTPUTS + index].setVoltage(voltage); | |||||
} | |||||
float ScriptEngine::getKnob(int index) { | |||||
return module->params[Prototype::KNOB_PARAMS + index].getValue(); | |||||
} | |||||
bool ScriptEngine::getSwitch(int index) { | |||||
return module->params[Prototype::SWITCH_PARAMS + index].getValue() > 0.f; | |||||
} | |||||
void ScriptEngine::setLight(int index, int color, float brightness) { | |||||
module->lights[Prototype::LIGHT_LIGHTS + index * 3 + color].setBrightness(brightness); | |||||
} | |||||
void ScriptEngine::setSwitchLight(int index, int color, float brightness) { | |||||
module->lights[Prototype::SWITCH_LIGHTS + index * 3 + color].setBrightness(brightness); | |||||
} | |||||
struct FileChoice : LedDisplayChoice { | struct FileChoice : LedDisplayChoice { | ||||
@@ -18,26 +18,26 @@ struct ScriptEngine { | |||||
*/ | */ | ||||
virtual int run(const std::string& path, const std::string& script) {return 0;} | virtual int run(const std::string& path, const std::string& script) {return 0;} | ||||
struct ProcessBlock { | |||||
float sampleRate = 0.f; | |||||
float sampleTime = 0.f; | |||||
float inputs[NUM_ROWS] = {}; | |||||
float outputs[NUM_ROWS] = {}; | |||||
float knobs[NUM_ROWS] = {}; | |||||
bool switches[NUM_ROWS] = {}; | |||||
float lights[NUM_ROWS][3] = {}; | |||||
float switchLights[NUM_ROWS][3] = {}; | |||||
struct ProcessArgs { | |||||
float sampleRate; | |||||
float sampleTime; | |||||
}; | }; | ||||
/** Calls the script's process() method. | /** Calls the script's process() method. | ||||
Return nonzero if failure, and set error message with setMessage(). | Return nonzero if failure, and set error message with setMessage(). | ||||
*/ | */ | ||||
virtual int process(ProcessBlock& block) {return 0;} | |||||
virtual int process(ProcessArgs& block) {return 0;} | |||||
// Communication with Prototype module. | // Communication with Prototype module. | ||||
// These cannot be called from the constructor, so initialize 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 setMessage(const std::string& message); | ||||
int getFrameDivider(); | int getFrameDivider(); | ||||
void setFrameDivider(int frameDivider); | void setFrameDivider(int frameDivider); | ||||
float getInput(int index); | |||||
void setOutput(int index, float voltage); | |||||
float getKnob(int index); | |||||
bool getSwitch(int index); | |||||
void setLight(int index, int color, float brightness); | |||||
void setSwitchLight(int index, int color, float brightness); | |||||
// private | // private | ||||
Prototype* module; | Prototype* module; | ||||
}; | }; | ||||