diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index b01cbcf1..da8f7af7 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -174,6 +174,7 @@ struct EngineWorker { struct Engine::Internal { std::vector modules; + /** Sorted by (inputModule, inputId) tuple */ std::vector cables; std::set paramHandles; Module* masterModule = NULL; @@ -184,10 +185,6 @@ struct Engine::Internal { std::map cablesCache; // (moduleId, paramId) std::map, ParamHandle*> paramHandlesCache; - /** Cache of cables connected to each input - Only connected inputs are allowed. - */ - std::map> inputCablesCache; float sampleRate = 0.f; float sampleTime = 0.f; @@ -318,6 +315,80 @@ static void Engine_stepWorker(Engine* that, int threadId) { } +static void Engine_stepFrameCables(Engine* that) { + auto finitize = [](float x) { + return std::isfinite(x) ? x : 0.f; + }; + + // Iterate each cable input group, since `cables` is sorted by input + auto firstIt = that->internal->cables.begin(); + while (firstIt != that->internal->cables.end()) { + Cable* firstCable = *firstIt; + Input* input = &firstCable->inputModule->inputs[firstCable->inputId]; + + // Find end of input group + auto endIt = firstIt; + while (++endIt != that->internal->cables.end()) { + Cable* endCable = *endIt; + // Check inputId first since it changes more frequently between cables + if (!(endCable->inputId == firstCable->inputId && endCable->inputModule == firstCable->inputModule)) + break; + } + + // Since stackable inputs are uncommon, only use stackable input logic if there are multiple cables in input group. + if (endIt - firstIt == 1) { + Output* output = &firstCable->outputModule->outputs[firstCable->outputId]; + // Copy all voltages from output to input + for (uint8_t c = 0; c < output->channels; c++) { + input->voltages[c] = finitize(output->voltages[c]); + } + // Set higher channel voltages to 0 + for (uint8_t c = output->channels; c < input->channels; c++) { + input->voltages[c] = 0.f; + } + input->channels = output->channels; + } + else { + // Calculate max output channels + uint8_t channels = 0; + for (auto it = firstIt; it < endIt; ++it) { + Cable* cable = *it; + Output* output = &cable->outputModule->outputs[cable->outputId]; + channels = std::max(channels, output->channels); + } + + // Clear input channels, including old channels + for (uint8_t c = 0; c < std::max(channels, input->channels); c++) { + input->voltages[c] = 0.f; + } + input->channels = channels; + + // Sum outputs of cables + for (auto it = firstIt; it < endIt; ++it) { + Cable* cable = *it; + Output* output = &cable->outputModule->outputs[cable->outputId]; + + // Sum monophonic value to all input channels + if (output->channels == 1) { + float value = finitize(output->voltages[0]); + for (uint8_t c = 0; c < channels; c++) { + input->voltages[c] += value; + } + } + // Sum polyphonic values to each input channel + else { + for (uint8_t c = 0; c < output->channels; c++) { + input->voltages[c] += finitize(output->voltages[c]); + } + } + } + } + + firstIt = endIt; + } +} + + /** Steps a single frame */ static void Engine_stepFrame(Engine* that) { @@ -350,44 +421,7 @@ static void Engine_stepFrame(Engine* that) { Engine_stepWorker(that, 0); internal->workerBarrier.wait(); - // Step cables for each input - for (const auto& pair : internal->inputCablesCache) { - Input* input = pair.first; - const std::vector& 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]; - - 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]); - } - } - } - } + Engine_stepFrameCables(that); // Flip messages for each module for (Module* module : that->internal->modules) { @@ -937,6 +971,10 @@ void Engine::addCable_NoLock(Cable* cable) { } // Add the cable internal->cables.push_back(cable); + // Sort cable by input so they are grouped in stepFrame() + std::sort(internal->cables.begin(), internal->cables.end(), [](Cable* a, Cable* b) { + return std::make_tuple(a->inputModule, a->inputId) < std::make_tuple(b->inputModule, b->inputId); + }); // Set default number of input/output channels if (!inputWasConnected) { input.channels = 1; @@ -946,7 +984,6 @@ void Engine::addCable_NoLock(Cable* cable) { } // Add caches internal->cablesCache[cable->id] = cable; - internal->inputCablesCache[&input].push_back(cable); // Dispatch input port event if (!inputWasConnected) { Module::PortChangeEvent e; @@ -980,16 +1017,6 @@ void Engine::removeCable_NoLock(Cable* cable) { auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); assert(it != internal->cables.end()); // 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);