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 { 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 { 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; float cpuTime = 0.0;


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


/** Override these to implement behavior when user clicks Initialize and Randomize */
virtual void initialize() {} virtual void initialize() {}
virtual void randomize() {} virtual void randomize() {}
}; };
@@ -43,10 +64,7 @@ struct Wire {
int outputId; int outputId;
Module *inputModule = NULL; Module *inputModule = NULL;
int inputId; 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(); void engineInit();
@@ -60,6 +78,7 @@ void engineRemoveModule(Module *module);
/** Does not transfer pointer ownership */ /** Does not transfer pointer ownership */
void engineAddWire(Wire *wire); void engineAddWire(Wire *wire);
void engineRemoveWire(Wire *wire); void engineRemoveWire(Wire *wire);
void engineSetParam(Module *module, int paramId, float value);
void engineSetParamSmooth(Module *module, int paramId, float value); void engineSetParamSmooth(Module *module, int paramId, float value);


extern float gSampleRate; extern float gSampleRate;


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

@@ -28,7 +28,7 @@ void ParamWidget::onChange() {
if (!module) if (!module)
return; 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()) { if (!inputBuffer.full()) {
Frame<8> f; Frame<8> f;
for (int i = 0; i < 8; i++) { 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); inputBuffer.push(f);
} }
@@ -166,7 +166,7 @@ void AudioInterface::step() {
if (!outputBuffer.empty()) { if (!outputBuffer.empty()) {
Frame<8> f = outputBuffer.shift(); Frame<8> f = outputBuffer.shift();
for (int i = 0; i < 8; i++) { 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() { int MidiInterface::getPortCount() {


+ 45
- 22
src/engine.cpp View File

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




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

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

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

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


@@ -133,7 +138,7 @@ void engineRemoveModule(Module *module) {
assert(module); assert(module);
VIPLock vipLock(vipMutex); VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex); 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) { if (module == smoothModule) {
smoothModule = NULL; smoothModule = NULL;
} }
@@ -142,45 +147,63 @@ void engineRemoveModule(Module *module) {
assert(wire->outputModule != module); assert(wire->outputModule != module);
assert(wire->inputModule != module); assert(wire->inputModule != module);
} }
// Check that the module actually exists
auto it = std::find(modules.begin(), modules.end(), module); auto it = std::find(modules.begin(), modules.end(), module);
assert(it != modules.end()); assert(it != modules.end());
// Remove it
modules.erase(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) { void engineAddWire(Wire *wire) {
assert(wire); assert(wire);
VIPLock vipLock(vipMutex); VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex); 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->outputModule);
assert(wire->inputModule); 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) { for (Wire *wire2 : wires) {
assert(wire2 != wire); assert(wire2 != wire);
assert(!(wire2->outputModule == wire->outputModule && wire2->outputId == wire->outputId));
assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId)); assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
} }
// Add the wire // Add the wire
wires.push_back(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) { void engineRemoveWire(Wire *wire) {
assert(wire); assert(wire);
VIPLock vipLock(vipMutex); VIPLock vipLock(vipMutex);
std::lock_guard<std::mutex> lock(mutex); 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); auto it = std::find(wires.begin(), wires.end(), wire);
assert(it != wires.end()); assert(it != wires.end());
// Set input to 0V
wire->inputModule->inputs[wire->inputId].value = 0.0;
// Remove the wire
wires.erase(it); 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) { 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); 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 // 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)) { if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
smoothModule->params[smoothParamId] = smoothValue;
smoothModule->params[smoothParamId].value = smoothValue;
} }
smoothModule = module; smoothModule = module;
smoothParamId = paramId; smoothParamId = paramId;


Loading…
Cancel
Save