Browse Source

Update readme. Add buffering to vco.js.

tags/v1.1.0
Andrew Belt 5 years ago
parent
commit
411d83f84f
4 changed files with 56 additions and 40 deletions
  1. +33
    -23
      README.md
  2. +5
    -5
      examples/rainbow.js
  3. +14
    -9
      examples/vco.js
  4. +4
    -3
      src/DuktapeEngine.cpp

+ 33
- 23
README.md View File

@@ -12,7 +12,7 @@ Scripting language host for [VCV Rack](https://vcvrack.com/) containing:
## Scripting API ## Scripting API


This is the reference API for the JavaScript script engine, along with default property values. 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]` vs `args.getInput(i)` vs `input(i)`), but the functionality should be similar.
Other script engines may vary in their syntax (e.g. `block.inputs[i]` vs `block.getInput(i)` vs `input(i)`), but the functionality should be similar.


```js ```js
/** Display message on LED display. /** Display message on LED display.
@@ -22,50 +22,60 @@ display(message)
/** Skip this many sample frames before running process(). /** Skip this many sample frames before running process().
For CV generators and processors, 256 is reasonable. 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 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 is recommended, but it will consume lots of CPU.
For audio generators and processors, 1 is recommended, but use `bufferSize` below.
If this is too slow for your purposes, consider writing a C++ plugin, since native VCV Rack plugins have 10-100x better performance. If this is too slow for your purposes, consider writing a C++ plugin, since native VCV Rack plugins have 10-100x better performance.
*/ */
config.frameDivider // 32 config.frameDivider // 32


/** Called when the next args is ready to be processed.
/** Instead of calling process() every sample frame, hold this many input/output voltages in a buffer and call process() when it is full.
This decreases CPU usage, since processing buffers is faster than processing one frame at a time.
The total latency of your script is `config.frameDivider * config.bufferSize * block.sampleTime`.
*/ */
function process(args) {
config.bufferSize // 1

/** Called when the next block is ready to be processed.
*/
function process(block) {
/** Engine sample rate in Hz. Read-only. /** Engine sample rate in Hz. Read-only.
*/ */
args.sampleRate
block.sampleRate


/** Engine sample timestep in seconds. Equal to `1 / sampleRate`. Read-only. /** Engine sample timestep in seconds. Equal to `1 / sampleRate`. Read-only.
Note that the actual time between process() calls is `args.sampleTime * config.frameDivider`.
Note that the actual time between process() calls is `block.sampleTime * config.frameDivider`.
*/
block.sampleTime

/** The actual size of the input/output buffers.
*/ */
args.sampleTime
block.bufferSize


/** Voltage of the input port of row `i`. Read-only.
/** Voltage of the input port of row `rowIndex`. Read-only.
*/ */
args.inputs[i] // 0.0
block.inputs[rowIndex][bufferIndex] // 0.0


/** Voltage of the output port of row `i`. Writable.
/** Voltage of the output port of row `rowIndex`. Writable.
*/ */
args.outputs[i] // 0.0
block.outputs[rowIndex][bufferIndex] // 0.0


/** Value of the knob of row `i`. Between 0 and 1. Read-only.
/** Value of the knob of row `rowIndex`. Between 0 and 1. Read-only.
*/ */
args.knobs[i] // 0.0
block.knobs[rowIndex] // 0.0


/** Pressed state of the switch of row `i`. Read-only.
/** Pressed state of the switch of row `rowIndex`. Read-only.
*/ */
args.switches[i] // false
block.switches[rowIndex] // false


/** Brightness of the RGB LED of row `i`. Writable.
/** Brightness of the RGB LED of row `rowIndex`, between 0 and 1. Writable.
*/ */
args.lights[i].r // 0.0
args.lights[i].g // 0.0
args.lights[i].b // 0.0
block.lights[rowIndex][0] // 0.0 (red)
block.lights[rowIndex][1] // 0.0 (green)
block.lights[rowIndex][2] // 0.0 (blue)


/** Brightness of the switch RGB LED of row `i`. Writable.
/** Brightness of the switch RGB LED of row `rowIndex`. Writable.
*/ */
args.switchLights[i].r // 0.0
args.switchLights[i].g // 0.0
args.switchLights[i].b // 0.0
block.switchLights[rowIndex][0] // 0.0 (red)
block.switchLights[rowIndex][1] // 0.0 (green)
block.switchLights[rowIndex][2] // 0.0 (blue)
} }
``` ```




+ 5
- 5
examples/rainbow.js View File

@@ -23,16 +23,16 @@ function hsvToRgb(h, s, v) {




var phase = 0 var phase = 0
function process(args) {
phase += args.sampleTime * config.frameDivider * 0.5
function process(block) {
phase += block.sampleTime * config.frameDivider * 0.5
phase %= 1 phase %= 1


for (var i = 0; i < 6; i++) { for (var i = 0; i < 6; i++) {
var h = (1 - 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.switchLights[i] = rgb
args.outputs[i][0] = Math.sin(2 * Math.PI * h) * 5 + 5
block.lights[i] = rgb
block.switchLights[i] = rgb
block.outputs[i][0] = Math.sin(2 * Math.PI * h) * 5 + 5
} }
} }




+ 14
- 9
examples/vco.js View File

@@ -4,13 +4,14 @@
// JavaScript isn't ideal for audio generating and processing due to it being 10-100 less efficient than C++, but it's still an easy way to learn simple DSP. // JavaScript isn't ideal for audio generating and processing due to it being 10-100 less efficient than C++, but it's still an easy way to learn simple DSP.


config.frameDivider = 1 config.frameDivider = 1
config.bufferSize = 16


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


// The relationship between 1V/oct pitch and frequency is `freq = 2^pitch`. // The relationship between 1V/oct pitch and frequency is `freq = 2^pitch`.
// Default frequency is middle C (C4) in Hz. // Default frequency is middle C (C4) in Hz.
@@ -18,11 +19,15 @@ function process(args) {
var freq = 261.6256 * Math.pow(2, pitch) var freq = 261.6256 * Math.pow(2, pitch)
display("Freq: " + freq.toFixed(3) + " Hz") display("Freq: " + freq.toFixed(3) + " Hz")


// Accumulate phase
phase += args.sampleTime * config.frameDivider * freq
// Wrap phase around range [0, 1]
phase %= 1
// Set all output samples in block
var deltaPhase = block.sampleTime * config.frameDivider * freq
for (var i = 0; i < block.bufferSize; i++) {
// Accumulate phase
phase += deltaPhase
// Wrap phase around range [0, 1]
phase %= 1


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

+ 4
- 3
src/DuktapeEngine.cpp View File

@@ -106,10 +106,7 @@ struct DuktapeEngine : ScriptEngine {
// block (keep on stack) // block (keep on stack)
duk_idx_t blockIdx = duk_push_object(ctx); duk_idx_t blockIdx = duk_push_object(ctx);
{ {
// bufferSize
int bufferSize = getBufferSize(); int bufferSize = getBufferSize();
duk_push_int(ctx, bufferSize);
duk_put_prop_string(ctx, blockIdx, "bufferSize");


// inputs // inputs
duk_idx_t inputsIdx = duk_push_array(ctx); duk_idx_t inputsIdx = duk_push_array(ctx);
@@ -191,6 +188,10 @@ struct DuktapeEngine : ScriptEngine {
duk_push_number(ctx, block.sampleTime); duk_push_number(ctx, block.sampleTime);
duk_put_prop_string(ctx, blockIdx, "sampleTime"); duk_put_prop_string(ctx, blockIdx, "sampleTime");


// bufferSize
duk_push_int(ctx, block.bufferSize);
duk_put_prop_string(ctx, blockIdx, "bufferSize");

// inputs // inputs
duk_get_prop_string(ctx, blockIdx, "inputs"); duk_get_prop_string(ctx, blockIdx, "inputs");
for (int i = 0; i < NUM_ROWS; i++) { for (int i = 0; i < NUM_ROWS; i++) {


Loading…
Cancel
Save