| @@ -12,6 +12,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. | |||
| - Add "Primary module" context menu item to VCV Audio modules to select which audio device clocks the engine. | |||
| - Allow other modules such as VCV Recorder to be the primary module, to render audio faster than real-time. | |||
| - Remove "Real-time priority" menu item, since the thread priority is now managed elsewhere (RtAudio, etc). | |||
| - Replace module disabling with bypassing, which directly routes certain inputs to outputs if specified by the plugin. | |||
| - Duplicate cables patched to inputs when a module is duplicated. | |||
| - Add module tags to module context menu. | |||
| - Add module manual URL (if plugin developer supplies it) to module context menu item. | |||
| @@ -82,7 +82,7 @@ struct ModuleWidget : widget::OpaqueWidget { | |||
| void randomizeAction(); | |||
| void disconnectAction(); | |||
| void cloneAction(); | |||
| void disableAction(); | |||
| void bypassAction(); | |||
| /** Deletes `this` */ | |||
| void removeAction(); | |||
| void createContextMenu(); | |||
| @@ -50,7 +50,7 @@ struct Engine { | |||
| Module* getModule(int moduleId); | |||
| void resetModule(Module* module); | |||
| void randomizeModule(Module* module); | |||
| void disableModule(Module* module, bool disabled); | |||
| void bypassModule(Module* module, bool bypassed); | |||
| // Cables | |||
| /** Adds a cable to the rack engine. | |||
| @@ -85,10 +85,7 @@ struct Module { | |||
| Only written when CPU timing is enabled, since time measurement is expensive. | |||
| */ | |||
| float cpuTime = 0.f; | |||
| /** Whether the Module is skipped from stepping by the engine. | |||
| Module subclasses should not read/write this variable. | |||
| */ | |||
| bool disabled = false; | |||
| bool bypassed = false; | |||
| /** Constructs a Module with no params, inputs, outputs, and lights. */ | |||
| Module(); | |||
| @@ -181,6 +178,7 @@ struct Module { | |||
| virtual void step() {} | |||
| /** Called instead of process() when Module is bypassed. | |||
| Typically you do not need to override this. Use configBypass() instead. | |||
| If you do override it, avoid reading param values, since the state of the module should have no effect on routing. | |||
| */ | |||
| virtual void processBypass(const ProcessArgs& args); | |||
| @@ -219,15 +217,15 @@ struct Module { | |||
| onRemove(); | |||
| } | |||
| struct EnableEvent {}; | |||
| /** Called after enabling the module. | |||
| struct BypassEvent {}; | |||
| /** Called after bypassing the module. | |||
| */ | |||
| virtual void onEnable(const EnableEvent& e) {} | |||
| virtual void onBypass(const BypassEvent& e) {} | |||
| struct DisableEvent {}; | |||
| /** Called after disabling the module. | |||
| struct UnBypassEvent {}; | |||
| /** Called after enabling the module. | |||
| */ | |||
| virtual void onDisable(const DisableEvent& e) {} | |||
| virtual void onUnBypass(const UnBypassEvent& e) {} | |||
| struct PortChangeEvent { | |||
| /** True if connecting, false if disconnecting. */ | |||
| @@ -97,12 +97,12 @@ struct ModuleMove : ModuleAction { | |||
| }; | |||
| struct ModuleDisable : ModuleAction { | |||
| bool disabled; | |||
| struct ModuleBypass : ModuleAction { | |||
| bool bypassed; | |||
| void undo() override; | |||
| void redo() override; | |||
| ModuleDisable() { | |||
| name = "disable module"; | |||
| ModuleBypass() { | |||
| name = "bypass module"; | |||
| } | |||
| }; | |||
| @@ -257,10 +257,10 @@ struct ModuleCloneItem : ui::MenuItem { | |||
| }; | |||
| struct ModuleDisableItem : ui::MenuItem { | |||
| struct ModuleBypassItem : ui::MenuItem { | |||
| ModuleWidget* moduleWidget; | |||
| void onAction(const event::Action& e) override { | |||
| moduleWidget->disableAction(); | |||
| moduleWidget->bypassAction(); | |||
| } | |||
| }; | |||
| @@ -299,7 +299,7 @@ ModuleWidget::~ModuleWidget() { | |||
| void ModuleWidget::draw(const DrawArgs& args) { | |||
| nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
| if (module && module->disabled) { | |||
| if (module && module->bypassed) { | |||
| nvgGlobalAlpha(args.vg, 0.33); | |||
| } | |||
| @@ -409,7 +409,7 @@ void ModuleWidget::onHoverKey(const event::HoverKey& e) { | |||
| } break; | |||
| case GLFW_KEY_E: { | |||
| if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
| disableAction(); | |||
| bypassAction(); | |||
| e.consume(this); | |||
| } | |||
| } break; | |||
| @@ -833,14 +833,14 @@ void ModuleWidget::cloneAction() { | |||
| APP->history->push(h); | |||
| } | |||
| void ModuleWidget::disableAction() { | |||
| void ModuleWidget::bypassAction() { | |||
| assert(module); | |||
| APP->engine->disableModule(module, !module->disabled); | |||
| APP->engine->bypassModule(module, !module->bypassed); | |||
| // history::ModuleDisable | |||
| history::ModuleDisable* h = new history::ModuleDisable; | |||
| // history::ModuleBypass | |||
| history::ModuleBypass* h = new history::ModuleBypass; | |||
| h->moduleId = module->id; | |||
| h->disabled = module->disabled; | |||
| h->bypassed = module->bypassed; | |||
| APP->history->push(h); | |||
| } | |||
| @@ -905,13 +905,13 @@ void ModuleWidget::createContextMenu() { | |||
| cloneItem->moduleWidget = this; | |||
| menu->addChild(cloneItem); | |||
| ModuleDisableItem* disableItem = new ModuleDisableItem; | |||
| disableItem->text = "Disable"; | |||
| disableItem->rightText = RACK_MOD_CTRL_NAME "+E"; | |||
| if (module && module->disabled) | |||
| disableItem->rightText = CHECKMARK_STRING " " + disableItem->rightText; | |||
| disableItem->moduleWidget = this; | |||
| menu->addChild(disableItem); | |||
| ModuleBypassItem* bypassItem = new ModuleBypassItem; | |||
| bypassItem->text = "Bypass"; | |||
| bypassItem->rightText = RACK_MOD_CTRL_NAME "+E"; | |||
| if (module && module->bypassed) | |||
| bypassItem->rightText = CHECKMARK_STRING " " + bypassItem->rightText; | |||
| bypassItem->moduleWidget = this; | |||
| menu->addChild(bypassItem); | |||
| ModuleDeleteItem* deleteItem = new ModuleDeleteItem; | |||
| deleteItem->text = "Delete"; | |||
| @@ -229,7 +229,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||
| } | |||
| // Step module | |||
| if (!module->disabled) | |||
| if (!module->bypassed) | |||
| module->process(processArgs); | |||
| else | |||
| module->processBypass(processArgs); | |||
| @@ -626,25 +626,25 @@ void Engine::randomizeModule(Module* module) { | |||
| } | |||
| void Engine::disableModule(Module* module, bool disabled) { | |||
| void Engine::bypassModule(Module* module, bool bypassed) { | |||
| std::lock_guard<std::recursive_mutex> lock(internal->mutex); | |||
| assert(module); | |||
| if (module->disabled == disabled) | |||
| 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->disabled = disabled; | |||
| module->bypassed = bypassed; | |||
| // Trigger event | |||
| if (disabled) { | |||
| Module::DisableEvent eDisable; | |||
| module->onDisable(eDisable); | |||
| if (bypassed) { | |||
| Module::BypassEvent eBypass; | |||
| module->onBypass(eBypass); | |||
| } | |||
| else { | |||
| Module::EnableEvent eEnable; | |||
| module->onEnable(eEnable); | |||
| Module::UnBypassEvent eUnBypass; | |||
| module->onUnBypass(eUnBypass); | |||
| } | |||
| } | |||
| @@ -91,9 +91,9 @@ json_t* Module::toJson() { | |||
| } | |||
| json_object_set_new(rootJ, "params", paramsJ); | |||
| // disabled | |||
| if (disabled) | |||
| json_object_set_new(rootJ, "disabled", json_boolean(disabled)); | |||
| // bypassed | |||
| if (bypassed) | |||
| json_object_set_new(rootJ, "bypassed", json_boolean(bypassed)); | |||
| // leftModuleId | |||
| if (leftExpander.moduleId >= 0) | |||
| @@ -183,13 +183,16 @@ void Module::fromJson(json_t* rootJ) { | |||
| params[paramId].setValue(json_number_value(valueJ)); | |||
| } | |||
| // disabled | |||
| json_t* disabledJ = json_object_get(rootJ, "disabled"); | |||
| // legacy bypass | |||
| if (!disabledJ) | |||
| disabledJ = json_object_get(rootJ, "bypass"); | |||
| if (disabledJ) | |||
| disabled = json_boolean_value(disabledJ); | |||
| // bypassed | |||
| json_t* bypassedJ = json_object_get(rootJ, "bypassed"); | |||
| // legacy "bypass" in v0.6 or early v1 (don't remember) | |||
| if (!bypassedJ) | |||
| bypassedJ = json_object_get(rootJ, "bypass"); | |||
| // legacy "disabled" in v1 | |||
| if (!bypassedJ) | |||
| bypassedJ = json_object_get(rootJ, "disabled"); | |||
| if (bypassedJ) | |||
| 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 | |||
| @@ -83,16 +83,16 @@ void ModuleMove::redo() { | |||
| } | |||
| void ModuleDisable::undo() { | |||
| void ModuleBypass::undo() { | |||
| engine::Module* module = APP->engine->getModule(moduleId); | |||
| assert(module); | |||
| APP->engine->disableModule(module, !disabled); | |||
| APP->engine->bypassModule(module, !bypassed); | |||
| } | |||
| void ModuleDisable::redo() { | |||
| void ModuleBypass::redo() { | |||
| engine::Module* module = APP->engine->getModule(moduleId); | |||
| assert(module); | |||
| APP->engine->disableModule(module, disabled); | |||
| APP->engine->bypassModule(module, bypassed); | |||
| } | |||
| @@ -102,7 +102,7 @@ void Plugin::fromJson(json_t* rootJ) { | |||
| size_t moduleId; | |||
| json_t* moduleJ; | |||
| json_array_foreach(modulesJ, moduleId, moduleJ) { | |||
| // Check if module is disabled | |||
| // Check if model is disabled | |||
| json_t* disabledJ = json_object_get(moduleJ, "disabled"); | |||
| if (disabledJ) { | |||
| if (json_boolean_value(disabledJ)) | |||