@@ -24,6 +24,9 @@ namespace engine { | |||
/** DSP processor instance for your module. */ | |||
struct Module { | |||
struct Internal; | |||
Internal* internal; | |||
plugin::Model* model = NULL; | |||
/** Unique ID for referring to the module in the engine. | |||
Assigned when added to the engine. | |||
@@ -81,12 +84,6 @@ struct Module { | |||
}; | |||
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. */ | |||
Module(); | |||
/** Use config() instead. */ | |||
@@ -276,6 +273,10 @@ struct Module { | |||
virtual void onRandomize() {} | |||
/** DEPRECATED. Override `onSampleRateChange(e)` instead. */ | |||
virtual void onSampleRateChange() {} | |||
// private | |||
float& cpuTime(); | |||
bool& bypassed(); | |||
}; | |||
@@ -299,7 +299,7 @@ ModuleWidget::~ModuleWidget() { | |||
void ModuleWidget::draw(const DrawArgs& args) { | |||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
if (module && module->bypassed) { | |||
if (module && module->bypassed()) { | |||
nvgGlobalAlpha(args.vg, 0.33); | |||
} | |||
@@ -314,12 +314,12 @@ void ModuleWidget::draw(const DrawArgs& args) { | |||
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75)); | |||
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); | |||
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); | |||
nvgRect(args.vg, | |||
0, (1.f - p) * box.size.y, | |||
@@ -835,12 +835,12 @@ void ModuleWidget::cloneAction() { | |||
void ModuleWidget::bypassAction() { | |||
assert(module); | |||
APP->engine->bypassModule(module, !module->bypassed); | |||
APP->engine->bypassModule(module, !module->bypassed()); | |||
// history::ModuleBypass | |||
history::ModuleBypass* h = new history::ModuleBypass; | |||
h->moduleId = module->id; | |||
h->bypassed = module->bypassed; | |||
h->bypassed = module->bypassed(); | |||
APP->history->push(h); | |||
} | |||
@@ -908,7 +908,7 @@ void ModuleWidget::createContextMenu() { | |||
ModuleBypassItem* bypassItem = new ModuleBypassItem; | |||
bypassItem->text = "Bypass"; | |||
bypassItem->rightText = RACK_MOD_CTRL_NAME "+E"; | |||
if (module && module->bypassed) | |||
if (module && module->bypassed()) | |||
bypassItem->rightText = CHECKMARK_STRING " " + bypassItem->rightText; | |||
bypassItem->moduleWidget = this; | |||
menu->addChild(bypassItem); | |||
@@ -229,7 +229,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||
} | |||
// Step module | |||
if (!module->bypassed) | |||
if (!module->bypassed()) | |||
module->process(processArgs); | |||
else | |||
module->processBypass(processArgs); | |||
@@ -241,7 +241,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||
// Smooth CPU time | |||
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 | |||
@@ -629,14 +629,14 @@ void Engine::randomizeModule(Module* module) { | |||
void Engine::bypassModule(Module* module, bool bypassed) { | |||
std::lock_guard<std::recursive_mutex> lock(internal->mutex); | |||
assert(module); | |||
if (module->bypassed == bypassed) | |||
if (module->bypassed() == bypassed) | |||
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->bypassed = bypassed; | |||
module->bypassed() = bypassed; | |||
// Trigger event | |||
if (bypassed) { | |||
Module::BypassEvent eBypass; | |||
@@ -6,7 +6,16 @@ namespace rack { | |||
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() { | |||
internal = new Internal; | |||
} | |||
Module::~Module() { | |||
@@ -22,6 +31,7 @@ Module::~Module() { | |||
if (outputInfo) | |||
delete outputInfo; | |||
} | |||
delete internal; | |||
} | |||
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); | |||
// 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 | |||
if (leftExpander.moduleId >= 0) | |||
@@ -192,7 +202,7 @@ void Module::fromJson(json_t* rootJ) { | |||
if (!bypassedJ) | |||
bypassedJ = json_object_get(rootJ, "disabled"); | |||
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. | |||
// // 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 rack |