@@ -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)) | |||