diff --git a/include/engine/Cable.hpp b/include/engine/Cable.hpp index 2013bc05..55f2b391 100644 --- a/include/engine/Cable.hpp +++ b/include/engine/Cable.hpp @@ -13,7 +13,6 @@ struct Cable { int outputId; Module *inputModule = NULL; int inputId; - void step(); }; diff --git a/include/engine/Port.hpp b/include/engine/Port.hpp index 0e7328b9..5a97ac53 100644 --- a/include/engine/Port.hpp +++ b/include/engine/Port.hpp @@ -23,8 +23,6 @@ struct alignas(32) Port { May be 0 to PORT_MAX_CHANNELS. */ uint8_t channels = 1; - /** Unstable API. Use isConnected() instead. */ - bool active = false; /** For rendering plug lights on cables. Green for positive, red for negative, and blue for polyphonic. */ @@ -79,6 +77,12 @@ struct alignas(32) Port { } } + void clearVoltages() { + for (int c = 0; c < channels; c++) { + voltages[c] = 0.f; + } + } + /** Returns the sum of all voltages. */ float getVoltageSum() { float sum = 0.f; @@ -90,10 +94,18 @@ struct alignas(32) Port { /** Sets the number of polyphony channels. */ void setChannels(int channels) { + // If disconnected, keep the number of channels at 0. + if (this->channels == 0) { + return; + } // Set higher channel voltages to 0 for (int c = channels; c < this->channels; c++) { voltages[c] = 0.f; } + // Don't allow caller to set port as disconnected + if (channels == 0) { + channels = 1; + } this->channels = channels; } @@ -105,7 +117,15 @@ struct alignas(32) Port { You can use this for skipping code that generates output voltages. */ bool isConnected() { - return active; + return channels > 0; + } + + bool isMonophonic() { + return channels == 1; + } + + bool isPolyphonic() { + return channels > 1; } void process(float deltaTime); diff --git a/src/engine/Cable.cpp b/src/engine/Cable.cpp deleted file mode 100644 index f6975a7a..00000000 --- a/src/engine/Cable.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - - -namespace rack { -namespace engine { - - -void Cable::step() { - Output *output = &outputModule->outputs[outputId]; - Input *input = &inputModule->inputs[inputId]; - // Match number of polyphonic channels to output port - input->channels = output->channels; - // Copy all voltages from output to input - for (int i = 0; i < PORT_MAX_CHANNELS; i++) { - input->voltages[i] = output->voltages[i]; - } -} - - -} // namespace engine -} // namespace rack diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index a36e53c2..f174df8a 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -268,6 +268,17 @@ static void Engine_stepModules(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 + input->channels = output->channels; + // Copy all voltages from output to input + for (int i = 0; i < PORT_MAX_CHANNELS; i++) { + input->voltages[i] = output->voltages[i]; + } +} + static void Engine_step(Engine *that) { Engine::Internal *internal = that->internal; @@ -292,15 +303,9 @@ static void Engine_step(Engine *that) { } } - // Step modules along with workers - internal->workerModuleIndex = 0; - internal->engineBarrier.wait(); - Engine_stepModules(that, 0); - internal->workerBarrier.wait(); - // Step cables for (Cable *cable : that->internal->cables) { - cable->step(); + Cable_step(cable); } // Flip messages for each module @@ -314,6 +319,12 @@ static void Engine_step(Engine *that) { module->rightExpander.messageFlipRequested = false; } } + + // Step modules along with workers + internal->workerModuleIndex = 0; + internal->engineBarrier.wait(); + Engine_stepModules(that, 0); + internal->workerBarrier.wait(); } static void Engine_updateExpander(Engine *that, Module::Expander *expander) { @@ -569,36 +580,58 @@ void Engine::bypassModule(Module *module, bool bypass) { assert(module); VIPLock vipLock(internal->vipMutex); std::lock_guard lock(internal->mutex); - if (bypass) { - for (Output &output : module->outputs) { - // This also zeros all voltages - output.setChannels(0); - } - module->cpuTime = 0.f; - } - else { - // Set all outputs to 1 channel - for (Output &output : module->outputs) { - output.setChannels(1); - } - } + if (module->bypass == bypass) + return; + // Clear outputs and set to 1 channel + for (Output &output : module->outputs) { + // This zeros all voltages, but the channel is set to 1 if connected + output.setChannels(0); + } + module->cpuTime = 0.f; module->bypass = bypass; } +static void Port_setDisconnected(Port *that) { + that->channels = 0; + for (int c = 0; c < PORT_MAX_CHANNELS; c++) { + that->voltages[c] = 0.f; + } +} + +static void Port_setConnected(Port *that) { + if (that->channels > 0) + return; + that->channels = 1; +} + static void Engine_updateConnected(Engine *that) { - // Set everything to unconnected + // Find disconnected ports + std::set disconnectedPorts; for (Module *module : that->internal->modules) { - for (Input &input : module->inputs) { - input.active = false; - } for (Output &output : module->outputs) { - output.active = false; + disconnectedPorts.insert(&output); + } + for (Input &input : module->inputs) { + disconnectedPorts.insert(&input); } } - // Set inputs/outputs to active for (Cable *cable : that->internal->cables) { - cable->outputModule->outputs[cable->outputId].active = true; - cable->inputModule->inputs[cable->inputId].active = true; + // Connect output + Output &output = cable->outputModule->outputs[cable->outputId]; + auto outputIt = disconnectedPorts.find(&output); + if (outputIt != disconnectedPorts.end()) + disconnectedPorts.erase(outputIt); + Port_setConnected(&output); + // Connect input + Input &input = cable->inputModule->inputs[cable->inputId]; + auto inputIt = disconnectedPorts.find(&input); + if (inputIt != disconnectedPorts.end()) + disconnectedPorts.erase(inputIt); + Port_setConnected(&input); + } + // Disconnect ports that have no cable + for (Port *port : disconnectedPorts) { + Port_setDisconnected(port); } } @@ -641,9 +674,6 @@ void Engine::removeCable(Cable *cable) { // Check that the cable is already added auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); assert(it != internal->cables.end()); - // Set input to inactive - Input &input = cable->inputModule->inputs[cable->inputId]; - input.setChannels(0); // Remove the cable internal->cables.erase(it); Engine_updateConnected(this); diff --git a/src/engine/Port.cpp b/src/engine/Port.cpp index 09cbee2b..79934203 100644 --- a/src/engine/Port.cpp +++ b/src/engine/Port.cpp @@ -7,12 +7,12 @@ namespace engine { void Port::process(float deltaTime) { // Set plug lights - if (!isConnected() || getChannels() == 0) { + if (channels == 0) { plugLights[0].setBrightness(0.f); plugLights[1].setBrightness(0.f); plugLights[2].setBrightness(0.f); } - else if (getChannels() == 1) { + else if (channels == 1) { float v = getVoltage() / 10.f; plugLights[0].setSmoothBrightness(v, deltaTime); plugLights[1].setSmoothBrightness(-v, deltaTime); @@ -20,7 +20,7 @@ void Port::process(float deltaTime) { } else { float v2 = 0.f; - for (int c = 0; c < getChannels(); c++) { + for (int c = 0; c < channels; c++) { v2 += std::pow(getVoltage(c), 2); } float v = std::sqrt(v2) / 10.f;