From e85e1d11bc90ce3ed64e8d3b9a1dcae36b0b2140 Mon Sep 17 00:00:00 2001 From: Cody Geary Date: Sun, 10 Nov 2024 00:05:07 +0100 Subject: [PATCH] 2.0.11 Update Added 'smart' knobs to Arrange and Morta. These allow the module to detect when a knob is being turned, and then disable parameter mapping in that case. --- plugin.json | 2 +- src/Arrange.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++---- src/Morta.cpp | 56 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/plugin.json b/plugin.json index d113d23..8c1aa1e 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "slug": "CVfunk", "name": "CV funk Modules", - "version": "2.0.10", + "version": "2.0.11", "license": "MIT", "brand": "CV funk", "author": "Cody Geary", diff --git a/src/Arrange.cpp b/src/Arrange.cpp index 5a17261..d54498c 100644 --- a/src/Arrange.cpp +++ b/src/Arrange.cpp @@ -67,6 +67,8 @@ struct Arrange : Module { NUM_LIGHTS }; + std::atomic isEditing[7]; //For the smart knobs + DigitalDisplay* digitalDisplay = nullptr; DigitalDisplay* chanDisplays[7] = {nullptr}; @@ -259,6 +261,10 @@ struct Arrange : Module { configOutput(CHAN_5_OUTPUT, "Channel 5"); configOutput(CHAN_6_OUTPUT, "Channel 6"); configOutput(CHAN_7_OUTPUT, "Channel 7"); + + for (int i = 0; i < 7; i++) { + isEditing[i] = false; // Initialize editing state to false + } } void onRandomize(const RandomizeEvent& e) override { @@ -383,7 +389,9 @@ struct Arrange : Module { for (int i = 0; i < 7; i++) { // Recall the output values for the current stage and set them to the knobs float recalledValue = outputValues[currentStage][i]; // Get the stored value for the current stage - paramQuantities[CHAN_1_KNOB + i]->setDisplayValue(recalledValue); + if (!isEditing[i]){ //only change the knob if it's not being turned + paramQuantities[CHAN_1_KNOB + i]->setDisplayValue(recalledValue); + } // Generate a random value for each gate based on the probability float randVal = random::uniform(); // Generate a random number between 0 and 1 @@ -452,13 +460,21 @@ struct Arrange : Module { float inputVal = knobVal; if (activeInputChannel[i]==i) { - inputVal = inputs[CHAN_1_INPUT + i].getPolyVoltage(0); + if (!isEditing[i]){ + inputVal = inputs[CHAN_1_INPUT + i].getPolyVoltage(0); + } else { + inputVal = knobVal; + } } else if (activeInputChannel[i] > -1){ // Now we compute which channel we need to grab int diffBetween = i - activeInputChannel[i]; int currentChannelMax = inputChannels[activeInputChannel[i]] ; if (currentChannelMax - diffBetween > 0) { //If we are before the last poly channel - inputVal = inputs[CHAN_1_INPUT + activeInputChannel[i]].getPolyVoltage(diffBetween); + if (!isEditing[i]){ + inputVal = inputs[CHAN_1_INPUT + activeInputChannel[i]].getPolyVoltage(diffBetween); + } else { + inputVal = knobVal; + } } } @@ -478,7 +494,9 @@ struct Arrange : Module { } if (knobVal != inputVal){ //only update knobs to the set value if they are different - paramQuantities[CHAN_1_KNOB + i]->setDisplayValue(inputVal); + if (!isEditing[i]){ //don't force set any knob currently being turned. + paramQuantities[CHAN_1_KNOB + i]->setDisplayValue(inputVal); + } } //Store the output value in the 2D array for the current stage @@ -585,6 +603,41 @@ struct ProgressDisplay : TransparentWidget { }; +//Define a SmartKnob that tracks if we are turning it +template +struct SmartKnob : BaseKnob { + void onDragStart(const event::DragStart& e) override { + if (ParamQuantity* paramQuantity = this->getParamQuantity()) { + if (Arrange* module = dynamic_cast(paramQuantity->module)) { + int index = paramQuantity->paramId - Arrange::CHAN_1_KNOB; //instance of 1st smart knob in the group + if (index >= 0 && index < 7) { //for 7 smart knobs + module->isEditing[index].store(true); + } + } + } + BaseKnob::onDragStart(e); + } + + void onDragEnd(const event::DragEnd& e) override { + if (ParamQuantity* paramQuantity = this->getParamQuantity()) { + if (Arrange* module = dynamic_cast(paramQuantity->module)) { + int index = paramQuantity->paramId - Arrange::CHAN_1_KNOB; + if (index >= 0 && index < 7) { //for 7 smart knobs + module->isEditing[index].store(false); + } + } + } + BaseKnob::onDragEnd(e); + } +}; + +// Type aliases to apply 'Smart' to all the knob types we use +using SmartRoundBlackKnob = SmartKnob; +using SmartTrimpot = SmartKnob; +using SmartRoundLargeBlackKnob = SmartKnob; +using SmartRoundHugeBlackKnob = SmartKnob; + + struct ArrangeWidget : ModuleWidget { ArrangeWidget(Arrange* module) { setModule(module); @@ -650,7 +703,7 @@ struct ArrangeWidget : ModuleWidget { addParam(createParamCentered (Vec(20 + 30, yPos), module, Arrange::CHAN_1_BUTTON + i)); addChild(createLightCentered>(Vec(20 + 30, yPos), module, Arrange::CHAN_1_LIGHT + i)); addChild(createLightCentered>(Vec(20 + 30, yPos), module, Arrange::CHAN_1_LIGHT_B + i)); - addParam(createParamCentered (Vec(50 + 35, yPos), module, Arrange::CHAN_1_KNOB + i)); + addParam(createParamCentered (Vec(50 + 35, yPos), module, Arrange::CHAN_1_KNOB + i)); if (module) { // Ratio Displays Initialization diff --git a/src/Morta.cpp b/src/Morta.cpp index a81e84e..8fb5f92 100644 --- a/src/Morta.cpp +++ b/src/Morta.cpp @@ -38,6 +38,8 @@ struct Morta : Module { NUM_LIGHTS }; + std::atomic isEditing[1]; //For the master smart knob + float inputValue = 0.f; float displayValue = 0.0f; DigitalDisplay* voltDisplay = nullptr; @@ -58,6 +60,8 @@ struct Morta : Module { for (int i = 0; i < 16; i++) { configOutput(OUTPUT_1_1 + i, "Output " + std::to_string(i / 4 + 1) + "," + std::to_string(i % 4 + 1)); } + + isEditing[0] = false; // Initialize editing state to false } void process(const ProcessArgs &args) override { @@ -81,9 +85,14 @@ struct Morta : Module { for (int c = 0; c < numChannels; c++) { // Override master knob value with input if input is connected + float topChannelVoltage = inputs[MAIN_INPUT].getVoltage(0); if (inputs[MAIN_INPUT].isConnected()) { - params[MASTER_KNOB].setValue(inputs[MAIN_INPUT].getVoltage(0)); - displayValue = inputs[MAIN_INPUT].getVoltage(0); + if (!isEditing[0]){ + params[MASTER_KNOB].setValue(topChannelVoltage); + displayValue = inputs[MAIN_INPUT].getVoltage(0); + } else { + displayValue = params[MASTER_KNOB].getValue(); + } } else { displayValue = params[MASTER_KNOB].getValue(); } @@ -97,7 +106,11 @@ struct Morta : Module { float inputValue = 0.0f; if (inputs[MAIN_INPUT].isConnected()) { - inputValue = inputs[MAIN_INPUT].getVoltage(c); + if (!isEditing[0]){ + inputValue = inputs[MAIN_INPUT].getVoltage(c); + } else { + inputValue = params[MASTER_KNOB].getValue(); + } } else { inputValue = params[MASTER_KNOB].getValue(); } @@ -124,6 +137,41 @@ struct Morta : Module { }; +//Define a SmartKnob that tracks if we are turning it +template +struct SmartKnob : BaseKnob { + void onDragStart(const event::DragStart& e) override { + if (ParamQuantity* paramQuantity = this->getParamQuantity()) { + if (Morta* module = dynamic_cast(paramQuantity->module)) { + int index = paramQuantity->paramId - Morta::MASTER_KNOB; //instance of 1st smart knob in the group + if (index >= 0 && index < 1) { //for 1 smart knobs + module->isEditing[index].store(true); + } + } + } + BaseKnob::onDragStart(e); + } + + void onDragEnd(const event::DragEnd& e) override { + if (ParamQuantity* paramQuantity = this->getParamQuantity()) { + if (Morta* module = dynamic_cast(paramQuantity->module)) { + int index = paramQuantity->paramId - Morta::MASTER_KNOB; + if (index >= 0 && index < 1) { //for 1 smart knobs + module->isEditing[index].store(false); + } + } + } + BaseKnob::onDragEnd(e); + } +}; + +// Type aliases to apply 'Smart' to all the knob types we use +using SmartRoundBlackKnob = SmartKnob; +using SmartTrimpot = SmartKnob; +using SmartRoundLargeBlackKnob = SmartKnob; +using SmartRoundHugeBlackKnob = SmartKnob; + + struct MortaWidget : ModuleWidget { MortaWidget(Morta* module) { setModule(module); @@ -142,7 +190,7 @@ struct MortaWidget : ModuleWidget { addInput(createInputCentered(Vec(box.size.x / 2 - 50, 70), module, Morta::MAIN_INPUT)); // Central giant knob - addParam(createParamCentered(Vec(box.size.x / 2, 70), module, Morta::MASTER_KNOB)); + addParam(createParamCentered(Vec(box.size.x / 2, 70), module, Morta::MASTER_KNOB)); // CV input, trimpot, and knob for the range control at the bottom addInput(createInputCentered(Vec(box.size.x / 2 + 30, 155), module, Morta::RANGE_CV_INPUT));