@@ -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. | - 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. | - 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). | - 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. | - Duplicate cables patched to inputs when a module is duplicated. | ||||
- Add module tags to module context menu. | - Add module tags to module context menu. | ||||
- Add module manual URL (if plugin developer supplies it) to module context menu item. | - 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 randomizeAction(); | ||||
void disconnectAction(); | void disconnectAction(); | ||||
void cloneAction(); | void cloneAction(); | ||||
void disableAction(); | |||||
void bypassAction(); | |||||
/** Deletes `this` */ | /** Deletes `this` */ | ||||
void removeAction(); | void removeAction(); | ||||
void createContextMenu(); | void createContextMenu(); | ||||
@@ -50,7 +50,7 @@ struct Engine { | |||||
Module* getModule(int moduleId); | Module* getModule(int moduleId); | ||||
void resetModule(Module* module); | void resetModule(Module* module); | ||||
void randomizeModule(Module* module); | void randomizeModule(Module* module); | ||||
void disableModule(Module* module, bool disabled); | |||||
void bypassModule(Module* module, bool bypassed); | |||||
// Cables | // Cables | ||||
/** Adds a cable to the rack engine. | /** 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. | Only written when CPU timing is enabled, since time measurement is expensive. | ||||
*/ | */ | ||||
float cpuTime = 0.f; | 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. */ | /** Constructs a Module with no params, inputs, outputs, and lights. */ | ||||
Module(); | Module(); | ||||
@@ -181,6 +178,7 @@ struct Module { | |||||
virtual void step() {} | virtual void step() {} | ||||
/** Called instead of process() when Module is bypassed. | /** Called instead of process() when Module is bypassed. | ||||
Typically you do not need to override this. Use configBypass() instead. | 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); | virtual void processBypass(const ProcessArgs& args); | ||||
@@ -219,15 +217,15 @@ struct Module { | |||||
onRemove(); | 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 { | struct PortChangeEvent { | ||||
/** True if connecting, false if disconnecting. */ | /** 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 undo() override; | ||||
void redo() 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; | ModuleWidget* moduleWidget; | ||||
void onAction(const event::Action& e) override { | void onAction(const event::Action& e) override { | ||||
moduleWidget->disableAction(); | |||||
moduleWidget->bypassAction(); | |||||
} | } | ||||
}; | }; | ||||
@@ -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->disabled) { | |||||
if (module && module->bypassed) { | |||||
nvgGlobalAlpha(args.vg, 0.33); | nvgGlobalAlpha(args.vg, 0.33); | ||||
} | } | ||||
@@ -409,7 +409,7 @@ void ModuleWidget::onHoverKey(const event::HoverKey& e) { | |||||
} break; | } break; | ||||
case GLFW_KEY_E: { | case GLFW_KEY_E: { | ||||
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
disableAction(); | |||||
bypassAction(); | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
} break; | } break; | ||||
@@ -833,14 +833,14 @@ void ModuleWidget::cloneAction() { | |||||
APP->history->push(h); | APP->history->push(h); | ||||
} | } | ||||
void ModuleWidget::disableAction() { | |||||
void ModuleWidget::bypassAction() { | |||||
assert(module); | 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->moduleId = module->id; | ||||
h->disabled = module->disabled; | |||||
h->bypassed = module->bypassed; | |||||
APP->history->push(h); | APP->history->push(h); | ||||
} | } | ||||
@@ -905,13 +905,13 @@ void ModuleWidget::createContextMenu() { | |||||
cloneItem->moduleWidget = this; | cloneItem->moduleWidget = this; | ||||
menu->addChild(cloneItem); | 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; | ModuleDeleteItem* deleteItem = new ModuleDeleteItem; | ||||
deleteItem->text = "Delete"; | deleteItem->text = "Delete"; | ||||
@@ -229,7 +229,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||||
} | } | ||||
// Step module | // Step module | ||||
if (!module->disabled) | |||||
if (!module->bypassed) | |||||
module->process(processArgs); | module->process(processArgs); | ||||
else | else | ||||
module->processBypass(processArgs); | 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); | std::lock_guard<std::recursive_mutex> lock(internal->mutex); | ||||
assert(module); | assert(module); | ||||
if (module->disabled == disabled) | |||||
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->disabled = disabled; | |||||
module->bypassed = bypassed; | |||||
// Trigger event | // Trigger event | ||||
if (disabled) { | |||||
Module::DisableEvent eDisable; | |||||
module->onDisable(eDisable); | |||||
if (bypassed) { | |||||
Module::BypassEvent eBypass; | |||||
module->onBypass(eBypass); | |||||
} | } | ||||
else { | 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); | 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 | // leftModuleId | ||||
if (leftExpander.moduleId >= 0) | if (leftExpander.moduleId >= 0) | ||||
@@ -183,13 +183,16 @@ void Module::fromJson(json_t* rootJ) { | |||||
params[paramId].setValue(json_number_value(valueJ)); | 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. | // These do not need to be deserialized, since the module positions will set them correctly when added to the rack. | ||||
// // leftModuleId | // // leftModuleId | ||||
@@ -83,16 +83,16 @@ void ModuleMove::redo() { | |||||
} | } | ||||
void ModuleDisable::undo() { | |||||
void ModuleBypass::undo() { | |||||
engine::Module* module = APP->engine->getModule(moduleId); | engine::Module* module = APP->engine->getModule(moduleId); | ||||
assert(module); | 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); | engine::Module* module = APP->engine->getModule(moduleId); | ||||
assert(module); | 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; | size_t moduleId; | ||||
json_t* moduleJ; | json_t* moduleJ; | ||||
json_array_foreach(modulesJ, moduleId, 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"); | json_t* disabledJ = json_object_get(moduleJ, "disabled"); | ||||
if (disabledJ) { | if (disabledJ) { | ||||
if (json_boolean_value(disabledJ)) | if (json_boolean_value(disabledJ)) | ||||