Browse Source

Allow multiple Cables per Input in Engine. Cache list of Cables connected to each Input.

tags/v2.5.0
Andrew Belt 8 months ago
parent
commit
c4a5c1016c
1 changed files with 87 additions and 48 deletions
  1. +87
    -48
      src/engine/Engine.cpp

+ 87
- 48
src/engine/Engine.cpp View File

@@ -184,6 +184,10 @@ struct Engine::Internal {
std::map<int64_t, Cable*> cablesCache;
// (moduleId, paramId)
std::map<std::tuple<int64_t, int>, ParamHandle*> paramHandlesCache;
/** Cache of cables connected to each input
Only connected inputs are allowed.
*/
std::map<Input*, std::vector<Cable*>> inputCablesCache;

float sampleRate = 0.f;
float sampleTime = 0.f;
@@ -319,27 +323,6 @@ static void Engine_stepWorker(Engine* that, int threadId) {
}


static void Cable_step(Cable* that) {
Output* output = &that->outputModule->outputs[that->outputId];
Input* input = &that->inputModule->inputs[that->inputId];
// Match number of polyphonic channels to output port
int channels = output->channels;
// Copy all voltages from output to input
for (int c = 0; c < channels; c++) {
float v = output->voltages[c];
// Set 0V if infinite or NaN
if (!std::isfinite(v))
v = 0.f;
input->voltages[c] = v;
}
// Set higher channel voltages to 0
for (int c = channels; c < input->channels; c++) {
input->voltages[c] = 0.f;
}
input->channels = channels;
}


/** Steps a single frame
*/
static void Engine_stepFrame(Engine* that) {
@@ -372,9 +355,44 @@ static void Engine_stepFrame(Engine* that) {
Engine_stepWorker(that, 0);
internal->workerBarrier.wait();

// Step cables
for (Cable* cable : that->internal->cables) {
Cable_step(cable);
// Step cables for each input
for (const auto& pair : internal->inputCablesCache) {
Input* input = pair.first;
const std::vector<Cable*>& cables = pair.second;
// Clear input voltages up to old number of input channels
for (int c = 0; c < input->channels; c++) {
input->voltages[c] = 0.f;
}
// Find max number of channels
uint8_t channels = 1;
for (Cable* cable : cables) {
Output* output = &cable->outputModule->outputs[cable->outputId];
channels = std::max(channels, output->channels);
}
input->channels = channels;
// Sum all outputs to input value
for (Cable* cable : cables) {
Output* output = &cable->outputModule->outputs[cable->outputId];
Input* input = &cable->inputModule->inputs[cable->inputId];

auto finitize = [](float x) {
return std::isfinite(x) ? x : 0.f;
};

// Sum monophonic value to all input channels
if (output->channels == 1) {
float value = finitize(output->voltages[0]);
for (int c = 0; c < channels; c++) {
input->voltages[c] += value;
}
}
// Sum polyphonic values to each input channel
else {
for (int c = 0; c < output->channels; c++) {
input->voltages[c] += finitize(output->voltages[c]);
}
}
}
}

// Flip messages for each module
@@ -898,34 +916,39 @@ void Engine::addCable_NoLock(Cable* cable) {
// Check cable properties
assert(cable->inputModule);
assert(cable->outputModule);
Input& input = cable->inputModule->inputs[cable->inputId];
Output& output = cable->outputModule->outputs[cable->outputId];
bool inputWasConnected = false;
bool outputWasConnected = false;
for (Cable* cable2 : internal->cables) {
// Check that the cable is not already added
assert(cable2 != cable);
// Check that the input is not already used by another cable
assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId));
// Check if input is already connected to a cable
if (cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId)
inputWasConnected = true;
// Check if output is already connected to a cable
if (cable2->outputModule == cable->outputModule && cable2->outputId == cable->outputId)
outputWasConnected = true;
}
// Set ID if unset or collides with an existing ID
while (cable->id < 0 || internal->cablesCache.find(cable->id) != internal->cablesCache.end()) {
// Randomly generate ID
// Generate random 52-bit ID
cable->id = random::u64() % (1ull << 53);
}
// Add the cable
internal->cables.push_back(cable);
internal->cablesCache[cable->id] = cable;
// Set input as connected
Input& input = cable->inputModule->inputs[cable->inputId];
input.channels = 1;
// Set output as connected, which might already be connected
Output& output = cable->outputModule->outputs[cable->outputId];
if (output.channels == 0) {
// Set default number of input/output channels
if (!inputWasConnected) {
input.channels = 1;
}
if (!outputWasConnected) {
output.channels = 1;
}
// Add caches
internal->cablesCache[cable->id] = cable;
internal->inputCablesCache[&input].push_back(cable);
// Dispatch input port event
{
if (!inputWasConnected) {
Module::PortChangeEvent e;
e.connecting = true;
e.type = Port::INPUT;
@@ -951,42 +974,58 @@ void Engine::removeCable(Cable* cable) {

void Engine::removeCable_NoLock(Cable* cable) {
assert(cable);
Input& input = cable->inputModule->inputs[cable->inputId];
Output& output = cable->outputModule->outputs[cable->outputId];
// Check that the cable is already added
auto it = std::find(internal->cables.begin(), internal->cables.end(), cable);
assert(it != internal->cables.end());
// Remove the cable
// Remove cable caches
{
auto v = internal->inputCablesCache[&input];
auto it = std::find(v.begin(), v.end(), cable);
assert(it != v.end());
v.erase(it);
// Remove input from cache if no cables are connected
if (v.empty()) {
internal->inputCablesCache.erase(&input);
}
}
internal->cablesCache.erase(cable->id);
// Remove cable
internal->cables.erase(it);
// Set input as disconnected
Input& input = cable->inputModule->inputs[cable->inputId];
input.channels = 0;
// Clear input values
for (uint8_t c = 0; c < PORT_MAX_CHANNELS; c++) {
input.setVoltage(0.f, c);
}
// Check if output is still connected to a cable
// Check if input/output is still connected to a cable
bool inputIsConnected = false;
bool outputIsConnected = false;
for (Cable* cable2 : internal->cables) {
if (cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId) {
inputIsConnected = true;
}
if (cable2->outputModule == cable->outputModule && cable2->outputId == cable->outputId) {
outputIsConnected = true;
break;
}
}
// Set input as disconnected if disconnected from all cables
if (!inputIsConnected) {
input.channels = 0;
// Clear input values
for (uint8_t c = 0; c < PORT_MAX_CHANNELS; c++) {
input.setVoltage(0.f, c);
}
}
// Set output as disconnected if disconnected from all cables
if (!outputIsConnected) {
Output& output = cable->outputModule->outputs[cable->outputId];
output.channels = 0;
// Don't clear output values
}
// Dispatch input port event
{
if (!inputIsConnected) {
Module::PortChangeEvent e;
e.connecting = false;
e.type = Port::INPUT;
e.portId = cable->inputId;
cable->inputModule->onPortChange(e);
}
// Dispatch output port event if its state went from connected to disconnected.
// Dispatch output port event
if (!outputIsConnected) {
Module::PortChangeEvent e;
e.connecting = false;


Loading…
Cancel
Save