Browse Source

Add engine support for stackable cables

tags/v0.4.0
Andrew Belt 7 years ago
parent
commit
fd0d208021
5 changed files with 87 additions and 52 deletions
  1. +30
    -11
      include/engine.hpp
  2. +1
    -1
      src/app/ParamWidget.cpp
  3. +2
    -2
      src/core/AudioInterface.cpp
  4. +9
    -16
      src/core/MidiInterface.cpp
  5. +45
    -22
      src/engine.cpp

+ 30
- 11
include/engine.hpp View File

@@ -7,14 +7,34 @@
namespace rack {


struct Param {
float value = 0.0;
};

struct Input {
/** Voltage of the port, zero if not plugged in. Read-only by Module */
float value = 0.0;
/** Whether a wire is plugged in */
bool active = false;
/** Returns the value if a wire is plugged in, otherwise returns the given default value */
float normalize(float normalValue) {
return active ? value : normalValue;
}
};

struct Output {
/** Voltage of the port. Write-only by Module */
float value = 0.0;
/** Whether a wire is plugged in */
bool active = false;
};


struct Module {
std::vector<float> params;
/** Pointers to voltage values at each port
If value is NULL, the input/output is disconnected
*/
std::vector<float*> inputs;
std::vector<float*> outputs;
/** For CPU usage */
std::vector<Param> params;
std::vector<Input> inputs;
std::vector<Output> outputs;
/** For CPU usage meter */
float cpuTime = 0.0;

/** Deprecated, use constructor below this one */
@@ -34,6 +54,7 @@ struct Module {
virtual json_t *toJson() { return NULL; }
virtual void fromJson(json_t *root) {}

/** Override these to implement behavior when user clicks Initialize and Randomize */
virtual void initialize() {}
virtual void randomize() {}
};
@@ -43,10 +64,7 @@ struct Wire {
int outputId;
Module *inputModule = NULL;
int inputId;
/** The voltage connected to input ports */
float inputValue = 0.0;
/** The voltage connected to output ports */
float outputValue = 0.0;
void step();
};

void engineInit();
@@ -60,6 +78,7 @@ void engineRemoveModule(Module *module);
/** Does not transfer pointer ownership */
void engineAddWire(Wire *wire);
void engineRemoveWire(Wire *wire);
void engineSetParam(Module *module, int paramId, float value);
void engineSetParamSmooth(Module *module, int paramId, float value);

extern float gSampleRate;


+ 1
- 1
src/app/ParamWidget.cpp View File

@@ -28,7 +28,7 @@ void ParamWidget::onChange() {
if (!module)
return;

module->params[paramId] = value;
engineSetParam(module, paramId, value);
}




+ 2
- 2
src/core/AudioInterface.cpp View File

@@ -145,7 +145,7 @@ void AudioInterface::step() {
if (!inputBuffer.full()) {
Frame<8> f;
for (int i = 0; i < 8; i++) {
f.samples[i] = getf(inputs[AUDIO1_INPUT + i]) / 5.0;
f.samples[i] = inputs[AUDIO1_INPUT + i].value / 5.0;
}
inputBuffer.push(f);
}
@@ -166,7 +166,7 @@ void AudioInterface::step() {
if (!outputBuffer.empty()) {
Frame<8> f = outputBuffer.shift();
for (int i = 0; i < 8; i++) {
setf(outputs[AUDIO1_OUTPUT + i], 5.0 * f.samples[i]);
outputs[AUDIO1_OUTPUT + i].value = 5.0 * f.samples[i];
}
}
}


+ 9
- 16
src/core/MidiInterface.cpp View File

@@ -115,23 +115,16 @@ void MidiInterface::step() {
}
}

if (outputs[PITCH_OUTPUT]) {
*outputs[PITCH_OUTPUT] = ((note - 60)) / 12.0;
}
if (outputs[GATE_OUTPUT]) {
bool gate = pedal || !notes.empty();
if (retrigger && retriggered) {
gate = false;
retriggered = false;
}
*outputs[GATE_OUTPUT] = gate ? 10.0 : 0.0;
}
if (outputs[MOD_OUTPUT]) {
*outputs[MOD_OUTPUT] = mod / 127.0 * 10.0;
}
if (outputs[PITCHWHEEL_OUTPUT]) {
*outputs[PITCHWHEEL_OUTPUT] = (pitchWheel - 64) / 64.0 * 10.0;
outputs[PITCH_OUTPUT].value = ((note - 60)) / 12.0;

bool gate = pedal || !notes.empty();
if (retrigger && retriggered) {
gate = false;
retriggered = false;
}
outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
outputs[MOD_OUTPUT].value = mod / 127.0 * 10.0;
outputs[PITCHWHEEL_OUTPUT].value = (pitchWheel - 64) / 64.0 * 10.0;
}

int MidiInterface::getPortCount() {


+ 45
- 22
src/engine.cpp View File

@@ -33,6 +33,11 @@ static int smoothParamId;
static float smoothValue;


void Wire::step() {
float value = outputModule->outputs[outputId].value;
inputModule->inputs[inputId].value = value;
}

void engineInit() {
gSampleRate = 44100.0;
}
@@ -46,28 +51,28 @@ void engineDestroy() {
static void engineStep() {
// Param interpolation
if (smoothModule) {
float value = smoothModule->params[smoothParamId];
float value = smoothModule->params[smoothParamId].value;
const float lambda = 60.0; // decay rate is 1 graphics frame
const float snap = 0.0001;
float delta = smoothValue - value;
if (fabsf(delta) < snap) {
smoothModule->params[smoothParamId] = smoothValue;
smoothModule->params[smoothParamId].value = smoothValue;
smoothModule = NULL;
}
else {
value += delta * lambda / gSampleRate;
smoothModule->params[smoothParamId] = value;
smoothModule->params[smoothParamId].value = value;
}
}

// Step modules
for (size_t i = 0; i < modules.size(); i++) {
Module *module = modules[i];
for (Module *module : modules) {
module->step();
}

// Step cables by moving their output values to inputs
for (Wire *wire : wires) {
wire->inputValue = wire->outputValue;
wire->outputValue = 0.0;
wire->step();
}
}

@@ -133,7 +138,7 @@ void engineRemoveModule(Module *module) {
assert(module);
VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex);
// If a param is being smoothed on this module, remove it immediately
// If a param is being smoothed on this module, stop smoothing it immediately
if (module == smoothModule) {
smoothModule = NULL;
}
@@ -142,45 +147,63 @@ void engineRemoveModule(Module *module) {
assert(wire->outputModule != module);
assert(wire->inputModule != module);
}
// Check that the module actually exists
auto it = std::find(modules.begin(), modules.end(), module);
assert(it != modules.end());
// Remove it
modules.erase(it);
}

static void updateActive() {
// Set everything to inactive
for (Module *module : modules) {
for (Input &input : module->inputs) {
input.active = false;
}
for (Output &output : module->outputs) {
output.active = false;
}
}
// Set inputs/outputs to active
for (Wire *wire : wires) {
wire->outputModule->outputs[wire->outputId].active = true;
wire->inputModule->outputs[wire->inputId].active = true;
}
}

void engineAddWire(Wire *wire) {
assert(wire);
VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex);
// Check that the wire is not already added
auto it = std::find(wires.begin(), wires.end(), wire);
assert(it == wires.end());
// Check wire properties
assert(wire->outputModule);
assert(wire->inputModule);
// Check that the inputs/outputs are not already used by another cable
// Check that the wire is not already added, and that the input is not already used by another cable
for (Wire *wire2 : wires) {
assert(wire2 != wire);
assert(!(wire2->outputModule == wire->outputModule && wire2->outputId == wire->outputId));
assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
}
// Add the wire
wires.push_back(wire);
// Connect the wire to inputModule
wire->inputModule->inputs[wire->inputId] = &wire->inputValue;
wire->outputModule->outputs[wire->outputId] = &wire->outputValue;
updateActive();
}

void engineRemoveWire(Wire *wire) {
assert(wire);
VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex);
// Disconnect wire from inputModule
wire->inputModule->inputs[wire->inputId] = NULL;
wire->outputModule->outputs[wire->outputId] = NULL;

// Remove the wire
// Check that the wire is already added
auto it = std::find(wires.begin(), wires.end(), wire);
assert(it != wires.end());
// Set input to 0V
wire->inputModule->inputs[wire->inputId].value = 0.0;
// Remove the wire
wires.erase(it);
updateActive();
}

void engineSetParam(Module *module, int paramId, float value) {
module->params[paramId].value = value;
}

void engineSetParamSmooth(Module *module, int paramId, float value) {
@@ -188,7 +211,7 @@ void engineSetParamSmooth(Module *module, int paramId, float value) {
std::lock_guard<std::mutex> lock(mutex);
// Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state
if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
smoothModule->params[smoothParamId] = smoothValue;
smoothModule->params[smoothParamId].value = smoothValue;
}
smoothModule = module;
smoothParamId = paramId;


Loading…
Cancel
Save