@@ -75,6 +75,12 @@ struct Module { | |||||
Expander leftExpander; | Expander leftExpander; | ||||
Expander rightExpander; | Expander rightExpander; | ||||
struct BypassRoute { | |||||
int inputId = -1; | |||||
int outputId = -1; | |||||
}; | |||||
std::vector<BypassRoute> bypassRoutes; | |||||
/** Seconds spent in the process() method, with exponential smoothing. | /** Seconds spent in the process() method, with exponential smoothing. | ||||
Only written when CPU timing is enabled, since time measurement is expensive. | Only written when CPU timing is enabled, since time measurement is expensive. | ||||
*/ | */ | ||||
@@ -146,6 +152,21 @@ struct Module { | |||||
outputInfos[portId] = p; | outputInfos[portId] = p; | ||||
} | } | ||||
/** Adds a direct route from an input to an output when the module is bypassed. */ | |||||
void configBypass(int inputId, int outputId) { | |||||
assert(inputId < (int) inputs.size()); | |||||
assert(outputId < (int) outputs.size()); | |||||
// Check that output is not yet routed | |||||
for (BypassRoute& br : bypassRoutes) { | |||||
assert(br.outputId != outputId); | |||||
} | |||||
BypassRoute br; | |||||
br.inputId = inputId; | |||||
br.outputId = outputId; | |||||
bypassRoutes.push_back(br); | |||||
} | |||||
struct ProcessArgs { | struct ProcessArgs { | ||||
float sampleRate; | float sampleRate; | ||||
float sampleTime; | float sampleTime; | ||||
@@ -158,6 +179,10 @@ struct Module { | |||||
} | } | ||||
/** DEPRECATED. Override `process(const ProcessArgs& args)` instead. */ | /** DEPRECATED. Override `process(const ProcessArgs& args)` instead. */ | ||||
virtual void step() {} | virtual void step() {} | ||||
/** Called instead of process() when Module is bypassed. | |||||
Typically you do not need to override this. Use configBypass() instead. | |||||
*/ | |||||
virtual void processBypass(const ProcessArgs& args); | |||||
json_t* toJson(); | json_t* toJson(); | ||||
/** This is virtual only for the purpose of unserializing legacy data when you could set properties of the `.modules[]` object itself. | /** This is virtual only for the purpose of unserializing legacy data when you could set properties of the `.modules[]` object itself. | ||||
@@ -306,7 +306,7 @@ void ModuleWidget::draw(const DrawArgs& args) { | |||||
Widget::draw(args); | Widget::draw(args); | ||||
// Power meter | // Power meter | ||||
if (module && settings::cpuMeter && !module->disabled) { | |||||
if (module && settings::cpuMeter) { | |||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
nvgRect(args.vg, | nvgRect(args.vg, | ||||
0, box.size.y - 35, | 0, box.size.y - 35, | ||||
@@ -220,21 +220,28 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||||
break; | break; | ||||
Module* module = internal->modules[i]; | Module* module = internal->modules[i]; | ||||
if (!module->disabled) { | |||||
// Step module | |||||
if (cpuMeter) { | |||||
auto beginTime = std::chrono::high_resolution_clock::now(); | |||||
module->process(processArgs); | |||||
auto endTime = std::chrono::high_resolution_clock::now(); | |||||
float duration = std::chrono::duration<float>(endTime - beginTime).count(); | |||||
// Smooth CPU time | |||||
const float cpuTau = 2.f /* seconds */; | |||||
module->cpuTime += (duration - module->cpuTime) * processArgs.sampleTime / cpuTau; | |||||
} | |||||
else { | |||||
module->process(processArgs); | |||||
} | |||||
// Start CPU timer | |||||
using time_point = std::chrono::time_point<std::chrono::high_resolution_clock>; | |||||
time_point beginTime; | |||||
if (cpuMeter) { | |||||
beginTime = std::chrono::high_resolution_clock::now(); | |||||
} | |||||
// Step module | |||||
if (!module->disabled) | |||||
module->process(processArgs); | |||||
else | |||||
module->processBypass(processArgs); | |||||
// Stop CPU timer | |||||
if (cpuMeter) { | |||||
time_point endTime = std::chrono::high_resolution_clock::now(); | |||||
float duration = std::chrono::duration<float>(endTime - beginTime).count(); | |||||
// Smooth CPU time | |||||
const float cpuTau = 2.f /* seconds */; | |||||
module->cpuTime += (duration - module->cpuTime) * processArgs.sampleTime / cpuTau; | |||||
} | } | ||||
// Iterate ports to step plug lights | // Iterate ports to step plug lights | ||||
@@ -47,6 +47,20 @@ void Module::config(int numParams, int numInputs, int numOutputs, int numLights) | |||||
} | } | ||||
} | } | ||||
void Module::processBypass(const ProcessArgs& args) { | |||||
for (BypassRoute& bypassRoute : bypassRoutes) { | |||||
// Route input voltages to output | |||||
Input& input = inputs[bypassRoute.inputId]; | |||||
Output& output = outputs[bypassRoute.outputId]; | |||||
int channels = input.getChannels(); | |||||
for (int c = 0; c < channels; c++) { | |||||
float v = input.getVoltage(c); | |||||
output.setVoltage(v, c); | |||||
} | |||||
output.setChannels(channels); | |||||
} | |||||
} | |||||
json_t* Module::toJson() { | json_t* Module::toJson() { | ||||
json_t* rootJ = json_object(); | json_t* rootJ = json_object(); | ||||