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

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
/** Display message on LED display.
@@ -22,50 +22,60 @@ display(message)
/** 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 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.
*/
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.
*/
args.sampleRate
block.sampleRate

/** 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
function process(args) {
phase += args.sampleTime * config.frameDivider * 0.5
function process(block) {
phase += block.sampleTime * config.frameDivider * 0.5
phase %= 1

for (var i = 0; i < 6; i++) {
var h = (1 - i / 6 + phase) % 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.

config.frameDivider = 1
config.bufferSize = 16

var phase = 0
function process(args) {
function process(block) {
// 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
pitch += args.inputs[0][0]
pitch += block.inputs[0][0]

// The relationship between 1V/oct pitch and frequency is `freq = 2^pitch`.
// Default frequency is middle C (C4) in Hz.
@@ -18,11 +19,15 @@ function process(args) {
var freq = 261.6256 * Math.pow(2, pitch)
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)
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);
@@ -191,6 +188,10 @@ struct DuktapeEngine : ScriptEngine {
duk_push_number(ctx, block.sampleTime);
duk_put_prop_string(ctx, blockIdx, "sampleTime");

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

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


Loading…
Cancel
Save