Browse Source

Add block buffering. Use array instead of object for RGB lights. Add changelog.

tags/v1.1.0
Andrew Belt 5 years ago
parent
commit
27a2c76da3
8 changed files with 161 additions and 100 deletions
  1. +9
    -0
      CHANGELOG.md
  2. +12
    -0
      Makefile
  3. +7
    -2
      examples/rainbow.js
  4. +2
    -2
      examples/vco.js
  5. +1
    -0
      plugin.json
  6. +67
    -59
      src/DuktapeEngine.cpp
  7. +51
    -29
      src/Prototype.cpp
  8. +12
    -8
      src/ScriptEngine.hpp

+ 9
- 0
CHANGELOG.md View File

@@ -0,0 +1,9 @@

### 1.1.0 (in development)
- Add block buffering with `config.bufferSize`.
- Scripts must change `inputs[i]` to `inputs[i][0]` and `outputs[i]` to `outputs[i][0]`.
- Duktape (JavaScript): Use array instead of object for RGB lights and switch lights.
- Scripts must change `.r` to `[0]`, `.g` to `[1]`, and `.b` to `[2]` for `lights` and `switchLights`.

### 1.0.0 (2019-09-15)
- Initial release.

+ 12
- 0
Makefile View File

@@ -21,7 +21,19 @@ FLAGS += -Idep/duktape-2.4.0/src

$(duktape):
cd dep && $(WGET) "https://duktape.org/duktape-2.4.0.tar.xz"
cd dep && $(SHA256) duktape-2.4.0.tar.xz 86a89307d1633b5cedb2c6e56dc86e92679fc34b05be551722d8cc69ab0771fc
cd dep && $(UNTAR) duktape-2.4.0.tar.xz

# # 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)


include $(RACK_DIR)/plugin.mk

+ 7
- 2
examples/rainbow.js View File

@@ -15,7 +15,10 @@ function hsvToRgb(h, s, v) {
else if (h < 5) rgb = [x, 0, c]
else rgb = [c, 0, x]
var m = v - c
return {r: rgb[0] + m, g: rgb[1] + m, b: rgb[2] + m}
rgb[0] += m
rgb[1] += m
rgb[2] += m
return rgb
}


@@ -29,8 +32,10 @@ function process(args) {
var rgb = hsvToRgb(h, 1, 1)
args.lights[i] = rgb
args.switchLights[i] = rgb
args.outputs[i] = Math.sin(2 * Math.PI * h) * 5 + 5
args.outputs[i][0] = Math.sin(2 * Math.PI * h) * 5 + 5
}
}

display("Hello, world!")

// 12.2us

+ 2
- 2
examples/vco.js View File

@@ -10,7 +10,7 @@ function process(args) {
// Knob ranges from -5 to 5 octaves
var pitch = args.knobs[0] * 10 - 5
// Input follows 1V/oct standard
pitch += args.inputs[0]
pitch += args.inputs[0][0]

// The relationship between 1V/oct pitch and frequency is `freq = 2^pitch`.
// Default frequency is middle C (C4) in Hz.
@@ -24,5 +24,5 @@ function process(args) {
phase %= 1

// Convert phase to sine output
args.outputs[0] = Math.sin(2 * Math.PI * phase) * 5
args.outputs[0][0] = Math.sin(2 * Math.PI * phase) * 5
}

+ 1
- 0
plugin.json View File

@@ -11,6 +11,7 @@
"manualUrl": "https://github.com/VCVRack/VCV-Prototype/blob/master/README.md",
"sourceUrl": "https://github.com/VCVRack/VCV-Prototype",
"donateUrl": "",
"changelogUrl": "https://github.com/VCVRack/VCV-Prototype/blob/master/CHANGELOG.md",
"modules": [
{
"slug": "Prototype",


+ 67
- 59
src/DuktapeEngine.cpp View File

@@ -56,6 +56,9 @@ struct DuktapeEngine : ScriptEngine {
// frameDivider
duk_push_int(ctx, getFrameDivider());
duk_put_prop_string(ctx, configIdx, "frameDivider");
// bufferSize
duk_push_int(ctx, getBufferSize());
duk_put_prop_string(ctx, configIdx, "bufferSize");
}
duk_put_global_string(ctx, "config");

@@ -86,6 +89,10 @@ struct DuktapeEngine : ScriptEngine {
duk_get_prop_string(ctx, -1, "frameDivider");
setFrameDivider(duk_get_int(ctx, -1));
duk_pop(ctx);
// bufferSize
duk_get_prop_string(ctx, -1, "bufferSize");
setBufferSize(duk_get_int(ctx, -1));
duk_pop(ctx);
}
duk_pop(ctx);

@@ -96,24 +103,37 @@ struct DuktapeEngine : ScriptEngine {
return -1;
}

// args (keep on stack)
duk_idx_t argsIdx = duk_push_object(ctx);
// block (keep on stack)
duk_idx_t blockIdx = duk_push_object(ctx);
{
// bufferSize
int bufferSize = getBufferSize();
duk_push_int(ctx, bufferSize);
duk_put_prop_string(ctx, blockIdx, "bufferSize");

// inputs
duk_idx_t inputsIdx = duk_push_array(ctx);
for (int i = 0; i < NUM_ROWS; i++) {
duk_push_number(ctx, 0.0);
duk_idx_t inputIdx = duk_push_array(ctx);
for (int j = 0; j < bufferSize; j++) {
duk_push_number(ctx, 0.0);
duk_put_prop_index(ctx, inputIdx, j);
}
duk_put_prop_index(ctx, inputsIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "inputs");
duk_put_prop_string(ctx, blockIdx, "inputs");

// outputs
duk_idx_t outputsIdx = duk_push_array(ctx);
for (int i = 0; i < NUM_ROWS; i++) {
duk_push_number(ctx, 0.0);
duk_idx_t outputIdx = duk_push_array(ctx);
for (int j = 0; j < bufferSize; j++) {
duk_push_number(ctx, 0.0);
duk_put_prop_index(ctx, outputIdx, j);
}
duk_put_prop_index(ctx, outputsIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "outputs");
duk_put_prop_string(ctx, blockIdx, "outputs");

// knobs
duk_idx_t knobsIdx = duk_push_array(ctx);
@@ -121,7 +141,7 @@ struct DuktapeEngine : ScriptEngine {
duk_push_number(ctx, 0.0);
duk_put_prop_index(ctx, knobsIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "knobs");
duk_put_prop_string(ctx, blockIdx, "knobs");

// switches
duk_idx_t switchesIdx = duk_push_array(ctx);
@@ -129,76 +149,72 @@ struct DuktapeEngine : ScriptEngine {
duk_push_number(ctx, 0.0);
duk_put_prop_index(ctx, switchesIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "switches");
duk_put_prop_string(ctx, blockIdx, "switches");

// lights
duk_idx_t lightsIdx = duk_push_array(ctx);
for (int i = 0; i < NUM_ROWS; i++) {
duk_idx_t lightIdx = duk_push_object(ctx);
{
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, lightIdx, "r");
duk_idx_t lightIdx = duk_push_array(ctx);
for (int c = 0; c < 3; c++) {
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, lightIdx, "g");
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, lightIdx, "b");
duk_put_prop_index(ctx, lightIdx, c);
}
duk_put_prop_index(ctx, lightsIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "lights");
duk_put_prop_string(ctx, blockIdx, "lights");

// switchLights
duk_idx_t switchLightsIdx = duk_push_array(ctx);
for (int i = 0; i < NUM_ROWS; i++) {
duk_idx_t switchLightIdx = duk_push_object(ctx);
{
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, switchLightIdx, "r");
duk_idx_t switchLightIdx = duk_push_array(ctx);
for (int c = 0; c < 3; c++) {
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, switchLightIdx, "g");
duk_push_number(ctx, 0.0);
duk_put_prop_string(ctx, switchLightIdx, "b");
duk_put_prop_index(ctx, switchLightIdx, c);
}
duk_put_prop_index(ctx, switchLightsIdx, i);
}
duk_put_prop_string(ctx, argsIdx, "switchLights");
duk_put_prop_string(ctx, blockIdx, "switchLights");
}

return 0;
}

int process(ProcessArgs& args) override {
// args
duk_idx_t argsIdx = duk_get_top(ctx) - 1;
int process(ProcessBlock& block) override {
// block
duk_idx_t blockIdx = duk_get_top(ctx) - 1;
{
// sampleRate
duk_push_number(ctx, args.sampleRate);
duk_put_prop_string(ctx, argsIdx, "sampleRate");
duk_push_number(ctx, block.sampleRate);
duk_put_prop_string(ctx, blockIdx, "sampleRate");

// sampleTime
duk_push_number(ctx, args.sampleTime);
duk_put_prop_string(ctx, argsIdx, "sampleTime");
duk_push_number(ctx, block.sampleTime);
duk_put_prop_string(ctx, blockIdx, "sampleTime");

// inputs
duk_get_prop_string(ctx, argsIdx, "inputs");
duk_get_prop_string(ctx, blockIdx, "inputs");
for (int i = 0; i < NUM_ROWS; i++) {
duk_push_number(ctx, getInput(i));
duk_put_prop_index(ctx, -2, i);
duk_get_prop_index(ctx, -1, i);
for (int j = 0; j < block.bufferSize; j++) {
duk_push_number(ctx, block.inputs[i][j]);
duk_put_prop_index(ctx, -2, j);
}
duk_pop(ctx);
}
duk_pop(ctx);

// knobs
duk_get_prop_string(ctx, argsIdx, "knobs");
duk_get_prop_string(ctx, blockIdx, "knobs");
for (int i = 0; i < NUM_ROWS; i++) {
duk_push_number(ctx, getKnob(i));
duk_push_number(ctx, block.knobs[i]);
duk_put_prop_index(ctx, -2, i);
}
duk_pop(ctx);

// switches
duk_get_prop_string(ctx, argsIdx, "switches");
duk_get_prop_string(ctx, blockIdx, "switches");
for (int i = 0; i < NUM_ROWS; i++) {
duk_push_boolean(ctx, getSwitch(i));
duk_push_boolean(ctx, block.switches[i]);
duk_put_prop_index(ctx, -2, i);
}
duk_pop(ctx);
@@ -206,7 +222,7 @@ struct DuktapeEngine : ScriptEngine {

// Duplicate process function
duk_dup(ctx, -2);
// Duplicate args object
// Duplicate block object
duk_dup(ctx, -2);
// Call process function
if (duk_pcall(ctx, 1)) {
@@ -219,13 +235,17 @@ struct DuktapeEngine : ScriptEngine {
// return value
duk_pop(ctx);

// args
// block
{
// outputs
duk_get_prop_string(ctx, -1, "outputs");
for (int i = 0; i < NUM_ROWS; i++) {
duk_get_prop_index(ctx, -1, i);
setOutput(i, duk_get_number(ctx, -1));
for (int j = 0; j < block.bufferSize; j++) {
duk_get_prop_index(ctx, -1, j);
block.outputs[i][j] = duk_get_number(ctx, -1);
duk_pop(ctx);
}
duk_pop(ctx);
}
duk_pop(ctx);
@@ -234,15 +254,9 @@ struct DuktapeEngine : ScriptEngine {
duk_get_prop_string(ctx, -1, "lights");
for (int i = 0; i < NUM_ROWS; i++) {
duk_get_prop_index(ctx, -1, i);
{
duk_get_prop_string(ctx, -1, "r");
setLight(i, 0, duk_get_number(ctx, -1));
duk_pop(ctx);
duk_get_prop_string(ctx, -1, "g");
setLight(i, 1, duk_get_number(ctx, -1));
duk_pop(ctx);
duk_get_prop_string(ctx, -1, "b");
setLight(i, 2, duk_get_number(ctx, -1));
for (int c = 0; c < 3; c++) {
duk_get_prop_index(ctx, -1, c);
block.lights[i][c] = duk_get_number(ctx, -1);
duk_pop(ctx);
}
duk_pop(ctx);
@@ -253,15 +267,9 @@ struct DuktapeEngine : ScriptEngine {
duk_get_prop_string(ctx, -1, "switchLights");
for (int i = 0; i < NUM_ROWS; i++) {
duk_get_prop_index(ctx, -1, i);
{
duk_get_prop_string(ctx, -1, "r");
setSwitchLight(i, 0, duk_get_number(ctx, -1));
duk_pop(ctx);
duk_get_prop_string(ctx, -1, "g");
setSwitchLight(i, 1, duk_get_number(ctx, -1));
duk_pop(ctx);
duk_get_prop_string(ctx, -1, "b");
setSwitchLight(i, 2, duk_get_number(ctx, -1));
for (int c = 0; c < 3; c++) {
duk_get_prop_index(ctx, -1, c);
block.switchLights[i][c] = duk_get_number(ctx, -1);
duk_pop(ctx);
}
duk_pop(ctx);


+ 51
- 29
src/Prototype.cpp View File

@@ -39,6 +39,8 @@ struct Prototype : Module {
ScriptEngine* scriptEngine = NULL;
int frame = 0;
int frameDivider;
ScriptEngine::ProcessBlock block;
int bufferIndex = 0;

Prototype() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -64,21 +66,49 @@ struct Prototype : Module {
return;
frame = 0;

ScriptEngine::ProcessArgs scriptArgs;
scriptArgs.sampleRate = args.sampleRate;
scriptArgs.sampleTime = args.sampleTime;

{
std::lock_guard<std::mutex> lock(scriptMutex);
// Check for certain inside the mutex
if (scriptEngine) {
if (scriptEngine->process(scriptArgs)) {
WARN("Script %s process() failed. Stopped script.", path.c_str());
clearScriptEngine();
return;
// Inputs
for (int i = 0; i < NUM_ROWS; i++)
block.inputs[i][bufferIndex] = inputs[IN_INPUTS + i].getVoltage();

// Process block
if (++bufferIndex >= block.bufferSize) {
bufferIndex = 0;

// Block settings
block.sampleRate = args.sampleRate;
block.sampleTime = args.sampleTime;

// 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;

// Run ScriptEngine's process function
{
std::lock_guard<std::mutex> lock(scriptMutex);
// Check for certain inside the mutex
if (scriptEngine) {
if (scriptEngine->process(block)) {
WARN("Script %s process() failed. Stopped script.", path.c_str());
clearScriptEngine();
return;
}
}
}

// 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]);
}

// Outputs
for (int i = 0; i < NUM_ROWS; i++)
outputs[OUT_OUTPUTS + i].setVoltage(block.outputs[i][bufferIndex]);
}

void clearScriptEngine() {
@@ -98,6 +128,9 @@ struct Prototype : Module {
// Reset settings
frameDivider = 32;
frame = 0;
block.bufferSize = 1;
std::memset(block.inputs, 0, sizeof(block.inputs));
bufferIndex = 0;
}

void setScriptString(std::string path, std::string script) {
@@ -183,23 +216,12 @@ int ScriptEngine::getFrameDivider() {
void ScriptEngine::setFrameDivider(int 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);
int ScriptEngine::getBufferSize() {
return module->block.bufferSize;
}
void ScriptEngine::setSwitchLight(int index, int color, float brightness) {
module->lights[Prototype::SWITCH_LIGHTS + index * 3 + color].setBrightness(brightness);
void ScriptEngine::setBufferSize(int bufferSize) {
bufferSize = clamp(bufferSize, 1, MAX_BUFFER_SIZE);
module->block.bufferSize = bufferSize;
}


@@ -219,7 +241,7 @@ struct FileChoice : LedDisplayChoice {
}

void onAction(const event::Action& e) override {
std::string dir = asset::user("");
std::string dir = asset::plugin(pluginInstance, "examples");
char* pathC = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
if (!pathC) {
return;


+ 12
- 8
src/ScriptEngine.hpp View File

@@ -3,6 +3,7 @@


static const int NUM_ROWS = 6;
static const int MAX_BUFFER_SIZE = 4096;


struct Prototype;
@@ -18,26 +19,29 @@ struct ScriptEngine {
*/
virtual int run(const std::string& path, const std::string& script) {return 0;}

struct ProcessArgs {
struct ProcessBlock {
float sampleRate;
float sampleTime;
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] = {};
};
/** Calls the script's process() method.
Return nonzero if failure, and set error message with setMessage().
*/
virtual int process(ProcessArgs& block) {return 0;}
virtual int process(ProcessBlock& block) {return 0;}

// 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);
int getFrameDivider();
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);
int getBufferSize();
void setBufferSize(int bufferSize);
// private
Prototype* module;
};


Loading…
Cancel
Save