@@ -24,6 +24,9 @@ namespace engine { | |||||
/** DSP processor instance for your module. */ | /** DSP processor instance for your module. */ | ||||
struct Module { | struct Module { | ||||
struct Internal; | |||||
Internal* internal; | |||||
plugin::Model* model = NULL; | plugin::Model* model = NULL; | ||||
/** Unique ID for referring to the module in the engine. | /** Unique ID for referring to the module in the engine. | ||||
Assigned when added to the engine. | Assigned when added to the engine. | ||||
@@ -81,12 +84,6 @@ struct Module { | |||||
}; | }; | ||||
std::vector<BypassRoute> bypassRoutes; | std::vector<BypassRoute> bypassRoutes; | ||||
/** Seconds spent in the process() method, with exponential smoothing. | |||||
Only written when CPU timing is enabled, since time measurement is expensive. | |||||
*/ | |||||
float cpuTime = 0.f; | |||||
bool bypassed = false; | |||||
/** Constructs a Module with no params, inputs, outputs, and lights. */ | /** Constructs a Module with no params, inputs, outputs, and lights. */ | ||||
Module(); | Module(); | ||||
/** Use config() instead. */ | /** Use config() instead. */ | ||||
@@ -276,6 +273,10 @@ struct Module { | |||||
virtual void onRandomize() {} | virtual void onRandomize() {} | ||||
/** DEPRECATED. Override `onSampleRateChange(e)` instead. */ | /** DEPRECATED. Override `onSampleRateChange(e)` instead. */ | ||||
virtual void onSampleRateChange() {} | virtual void onSampleRateChange() {} | ||||
// private | |||||
float& cpuTime(); | |||||
bool& bypassed(); | |||||
}; | }; | ||||
@@ -299,7 +299,7 @@ ModuleWidget::~ModuleWidget() { | |||||
void ModuleWidget::draw(const DrawArgs& args) { | void ModuleWidget::draw(const DrawArgs& args) { | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
if (module && module->bypassed) { | |||||
if (module && module->bypassed()) { | |||||
nvgGlobalAlpha(args.vg, 0.33); | nvgGlobalAlpha(args.vg, 0.33); | ||||
} | } | ||||
@@ -314,12 +314,12 @@ void ModuleWidget::draw(const DrawArgs& args) { | |||||
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75)); | nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75)); | ||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
float percent = module->cpuTime * APP->engine->getSampleRate() * 100; | |||||
float microseconds = module->cpuTime * 1e6f; | |||||
float percent = module->cpuTime() * APP->engine->getSampleRate() * 100; | |||||
float microseconds = module->cpuTime() * 1e6f; | |||||
std::string cpuText = string::f("%.1f%%\n%.2f μs", percent, microseconds); | std::string cpuText = string::f("%.1f%%\n%.2f μs", percent, microseconds); | ||||
bndLabel(args.vg, 2.0, box.size.y - 34.0, INFINITY, INFINITY, -1, cpuText.c_str()); | bndLabel(args.vg, 2.0, box.size.y - 34.0, INFINITY, INFINITY, -1, cpuText.c_str()); | ||||
float p = math::clamp(module->cpuTime / APP->engine->getSampleTime(), 0.f, 1.f); | |||||
float p = math::clamp(module->cpuTime() / APP->engine->getSampleTime(), 0.f, 1.f); | |||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
nvgRect(args.vg, | nvgRect(args.vg, | ||||
0, (1.f - p) * box.size.y, | 0, (1.f - p) * box.size.y, | ||||
@@ -835,12 +835,12 @@ void ModuleWidget::cloneAction() { | |||||
void ModuleWidget::bypassAction() { | void ModuleWidget::bypassAction() { | ||||
assert(module); | assert(module); | ||||
APP->engine->bypassModule(module, !module->bypassed); | |||||
APP->engine->bypassModule(module, !module->bypassed()); | |||||
// history::ModuleBypass | // history::ModuleBypass | ||||
history::ModuleBypass* h = new history::ModuleBypass; | history::ModuleBypass* h = new history::ModuleBypass; | ||||
h->moduleId = module->id; | h->moduleId = module->id; | ||||
h->bypassed = module->bypassed; | |||||
h->bypassed = module->bypassed(); | |||||
APP->history->push(h); | APP->history->push(h); | ||||
} | } | ||||
@@ -908,7 +908,7 @@ void ModuleWidget::createContextMenu() { | |||||
ModuleBypassItem* bypassItem = new ModuleBypassItem; | ModuleBypassItem* bypassItem = new ModuleBypassItem; | ||||
bypassItem->text = "Bypass"; | bypassItem->text = "Bypass"; | ||||
bypassItem->rightText = RACK_MOD_CTRL_NAME "+E"; | bypassItem->rightText = RACK_MOD_CTRL_NAME "+E"; | ||||
if (module && module->bypassed) | |||||
if (module && module->bypassed()) | |||||
bypassItem->rightText = CHECKMARK_STRING " " + bypassItem->rightText; | bypassItem->rightText = CHECKMARK_STRING " " + bypassItem->rightText; | ||||
bypassItem->moduleWidget = this; | bypassItem->moduleWidget = this; | ||||
menu->addChild(bypassItem); | menu->addChild(bypassItem); | ||||
@@ -229,7 +229,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||||
} | } | ||||
// Step module | // Step module | ||||
if (!module->bypassed) | |||||
if (!module->bypassed()) | |||||
module->process(processArgs); | module->process(processArgs); | ||||
else | else | ||||
module->processBypass(processArgs); | module->processBypass(processArgs); | ||||
@@ -241,7 +241,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||||
// Smooth CPU time | // Smooth CPU time | ||||
const float cpuTau = 2.f /* seconds */; | const float cpuTau = 2.f /* seconds */; | ||||
module->cpuTime += (duration - module->cpuTime) * processArgs.sampleTime / cpuTau; | |||||
module->cpuTime() += (duration - module->cpuTime()) * processArgs.sampleTime / cpuTau; | |||||
} | } | ||||
// Iterate ports to step plug lights | // Iterate ports to step plug lights | ||||
@@ -629,14 +629,14 @@ void Engine::randomizeModule(Module* module) { | |||||
void Engine::bypassModule(Module* module, bool bypassed) { | void Engine::bypassModule(Module* module, bool bypassed) { | ||||
std::lock_guard<std::recursive_mutex> lock(internal->mutex); | std::lock_guard<std::recursive_mutex> lock(internal->mutex); | ||||
assert(module); | assert(module); | ||||
if (module->bypassed == bypassed) | |||||
if (module->bypassed() == bypassed) | |||||
return; | return; | ||||
// Clear outputs and set to 1 channel | // Clear outputs and set to 1 channel | ||||
for (Output& output : module->outputs) { | for (Output& output : module->outputs) { | ||||
// This zeros all voltages, but the channel is set to 1 if connected | // This zeros all voltages, but the channel is set to 1 if connected | ||||
output.setChannels(0); | output.setChannels(0); | ||||
} | } | ||||
module->bypassed = bypassed; | |||||
module->bypassed() = bypassed; | |||||
// Trigger event | // Trigger event | ||||
if (bypassed) { | if (bypassed) { | ||||
Module::BypassEvent eBypass; | Module::BypassEvent eBypass; | ||||
@@ -6,7 +6,16 @@ namespace rack { | |||||
namespace engine { | namespace engine { | ||||
struct Module::Internal { | |||||
/** Seconds spent in the process() method, with exponential smoothing. | |||||
Only written when CPU timing is enabled, since time measurement is expensive. | |||||
*/ | |||||
float cpuTime = 0.f; | |||||
bool bypassed = false; | |||||
}; | |||||
Module::Module() { | Module::Module() { | ||||
internal = new Internal; | |||||
} | } | ||||
Module::~Module() { | Module::~Module() { | ||||
@@ -22,6 +31,7 @@ Module::~Module() { | |||||
if (outputInfo) | if (outputInfo) | ||||
delete outputInfo; | delete outputInfo; | ||||
} | } | ||||
delete internal; | |||||
} | } | ||||
void Module::config(int numParams, int numInputs, int numOutputs, int numLights) { | void Module::config(int numParams, int numInputs, int numOutputs, int numLights) { | ||||
@@ -92,8 +102,8 @@ json_t* Module::toJson() { | |||||
json_object_set_new(rootJ, "params", paramsJ); | json_object_set_new(rootJ, "params", paramsJ); | ||||
// bypassed | // bypassed | ||||
if (bypassed) | |||||
json_object_set_new(rootJ, "bypassed", json_boolean(bypassed)); | |||||
if (internal->bypassed) | |||||
json_object_set_new(rootJ, "bypassed", json_boolean(true)); | |||||
// leftModuleId | // leftModuleId | ||||
if (leftExpander.moduleId >= 0) | if (leftExpander.moduleId >= 0) | ||||
@@ -192,7 +202,7 @@ void Module::fromJson(json_t* rootJ) { | |||||
if (!bypassedJ) | if (!bypassedJ) | ||||
bypassedJ = json_object_get(rootJ, "disabled"); | bypassedJ = json_object_get(rootJ, "disabled"); | ||||
if (bypassedJ) | if (bypassedJ) | ||||
bypassed = json_boolean_value(bypassedJ); | |||||
internal->bypassed = json_boolean_value(bypassedJ); | |||||
// These do not need to be deserialized, since the module positions will set them correctly when added to the rack. | // These do not need to be deserialized, since the module positions will set them correctly when added to the rack. | ||||
// // leftModuleId | // // leftModuleId | ||||
@@ -236,5 +246,15 @@ void Module::onRandomize(const RandomizeEvent& e) { | |||||
} | } | ||||
float& Module::cpuTime() { | |||||
return internal->cpuTime; | |||||
} | |||||
bool& Module::bypassed() { | |||||
return internal->bypassed; | |||||
} | |||||
} // namespace engine | } // namespace engine | ||||
} // namespace rack | } // namespace rack |