@@ -38,6 +38,7 @@ struct ModuleWidget : OpaqueWidget { | |||||
void save(std::string filename); | void save(std::string filename); | ||||
void loadDialog(); | void loadDialog(); | ||||
void saveDialog(); | void saveDialog(); | ||||
void toggleBypass(); | |||||
/** Disconnects cables from all ports | /** Disconnects cables from all ports | ||||
Called when the user clicks Disconnect Cables in the context menu. | Called when the user clicks Disconnect Cables in the context menu. | ||||
@@ -20,6 +20,7 @@ struct Module { | |||||
std::vector<Light> lights; | std::vector<Light> lights; | ||||
/** For power meter */ | /** For power meter */ | ||||
float cpuTime = 0.f; | float cpuTime = 0.f; | ||||
bool bypass = false; | |||||
/** Constructs a Module with no params, inputs, outputs, and lights */ | /** Constructs a Module with no params, inputs, outputs, and lights */ | ||||
Module(); | Module(); | ||||
@@ -250,6 +250,12 @@ void ModuleWidget::saveDialog() { | |||||
save(pathStr); | save(pathStr); | ||||
} | } | ||||
void ModuleWidget::toggleBypass() { | |||||
if (!module) | |||||
return; | |||||
module->bypass ^= true; | |||||
} | |||||
void ModuleWidget::disconnect() { | void ModuleWidget::disconnect() { | ||||
for (PortWidget *input : inputs) { | for (PortWidget *input : inputs) { | ||||
context()->scene->rackWidget->wireContainer->removeAllWires(input); | context()->scene->rackWidget->wireContainer->removeAllWires(input); | ||||
@@ -272,6 +278,9 @@ void ModuleWidget::randomize() { | |||||
} | } | ||||
void ModuleWidget::draw(NVGcontext *vg) { | void ModuleWidget::draw(NVGcontext *vg) { | ||||
if (module && module->bypass) { | |||||
nvgGlobalAlpha(vg, 0.5); | |||||
} | |||||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | nvgScissor(vg, 0, 0, box.size.x, box.size.y); | ||||
Widget::draw(vg); | Widget::draw(vg); | ||||
@@ -375,6 +384,12 @@ void ModuleWidget::onHoverKey(const event::HoverKey &e) { | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_E: { | |||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | |||||
toggleBypass(); | |||||
e.consume(this); | |||||
} | |||||
} break; | |||||
} | } | ||||
} | } | ||||
@@ -456,7 +471,7 @@ struct ModulePasteItem : MenuItem { | |||||
struct ModuleSaveItem : MenuItem { | struct ModuleSaveItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
ModuleSaveItem() { | ModuleSaveItem() { | ||||
text = "Save preset"; | |||||
text = "Save preset as"; | |||||
} | } | ||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
moduleWidget->saveDialog(); | moduleWidget->saveDialog(); | ||||
@@ -484,6 +499,23 @@ struct ModuleCloneItem : MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct ModuleBypassItem : MenuItem { | |||||
ModuleWidget *moduleWidget; | |||||
ModuleBypassItem() { | |||||
text = "Bypass"; | |||||
} | |||||
void step() override { | |||||
rightText = WINDOW_MOD_KEY_NAME "+E"; | |||||
if (!moduleWidget->module) | |||||
return; | |||||
if (moduleWidget->module->bypass) | |||||
rightText = CHECKMARK_STRING " " + rightText; | |||||
} | |||||
void onAction(const event::Action &e) override { | |||||
moduleWidget->toggleBypass(); | |||||
} | |||||
}; | |||||
struct ModuleDeleteItem : MenuItem { | struct ModuleDeleteItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
ModuleDeleteItem() { | ModuleDeleteItem() { | ||||
@@ -535,6 +567,10 @@ Menu *ModuleWidget::createContextMenu() { | |||||
saveItem->moduleWidget = this; | saveItem->moduleWidget = this; | ||||
menu->addChild(saveItem); | menu->addChild(saveItem); | ||||
ModuleBypassItem *bypassItem = new ModuleBypassItem; | |||||
bypassItem->moduleWidget = this; | |||||
menu->addChild(bypassItem); | |||||
ModuleDeleteItem *deleteItem = new ModuleDeleteItem; | ModuleDeleteItem *deleteItem = new ModuleDeleteItem; | ||||
deleteItem->moduleWidget = this; | deleteItem->moduleWidget = this; | ||||
menu->addChild(deleteItem); | menu->addChild(deleteItem); | ||||
@@ -96,6 +96,17 @@ struct DisconnectCablesItem : MenuItem { | |||||
}; | }; | ||||
struct QuitItem : MenuItem { | |||||
QuitItem() { | |||||
text = "Quit"; | |||||
rightText = "(" WINDOW_MOD_KEY_NAME "+Q)"; | |||||
} | |||||
void onAction(const event::Action &e) override { | |||||
context()->window->close(); | |||||
} | |||||
}; | |||||
struct FileButton : MenuButton { | struct FileButton : MenuButton { | ||||
FileButton() { | FileButton() { | ||||
text = "File"; | text = "File"; | ||||
@@ -111,6 +122,7 @@ struct FileButton : MenuButton { | |||||
menu->addChild(new SaveAsItem); | menu->addChild(new SaveAsItem); | ||||
menu->addChild(new RevertItem); | menu->addChild(new RevertItem); | ||||
menu->addChild(new DisconnectCablesItem); | menu->addChild(new DisconnectCablesItem); | ||||
menu->addChild(new QuitItem); | |||||
} | } | ||||
}; | }; | ||||
@@ -128,18 +128,27 @@ static void Engine_step(Engine *engine) { | |||||
// Step modules | // Step modules | ||||
for (Module *module : engine->modules) { | for (Module *module : engine->modules) { | ||||
if (settings::powerMeter) { | |||||
auto startTime = std::chrono::high_resolution_clock::now(); | |||||
module->step(); | |||||
auto stopTime = std::chrono::high_resolution_clock::now(); | |||||
float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * engine->internal->sampleRate; | |||||
// Smooth cpu time | |||||
module->cpuTime += (cpuTime - module->cpuTime) * engine->internal->sampleTime / 0.5f; | |||||
if (module->bypass) { | |||||
for (Output &output : module->outputs) { | |||||
output.numChannels = 1; | |||||
output.setValue(0.f); | |||||
} | |||||
module->cpuTime = 0.f; | |||||
} | } | ||||
else { | else { | ||||
module->step(); | |||||
if (settings::powerMeter) { | |||||
auto startTime = std::chrono::high_resolution_clock::now(); | |||||
module->step(); | |||||
auto stopTime = std::chrono::high_resolution_clock::now(); | |||||
float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * engine->internal->sampleRate; | |||||
// Smooth cpu time | |||||
module->cpuTime += (cpuTime - module->cpuTime) * engine->internal->sampleTime / 0.5f; | |||||
} | |||||
else { | |||||
module->step(); | |||||
} | |||||
} | } | ||||
// Step ports | // Step ports | ||||
@@ -78,7 +78,7 @@ void Plugin::fromJson(json_t *rootJ) { | |||||
json_object_foreach(modulesJ, slug, moduleJ) { | json_object_foreach(modulesJ, slug, moduleJ) { | ||||
Model *model = getModel(slug); | Model *model = getModel(slug); | ||||
if (!model) { | if (!model) { | ||||
WARN("Metadata references module \"%s\" but it is not registered in the plugin library", slug); | |||||
WARN("plugin.json contains module \"%s\" but it is not defined in the plugin", slug); | |||||
continue; | continue; | ||||
} | } | ||||