ComputerscareTolyPools-v2.cpp (8718B)
1 #include "Computerscare.hpp" 2 #include "dtpulse.hpp" 3 4 struct ComputerscareTolyPoolsV2; 5 6 /* 7 Input: 8 9 first rotate 10 knob, CV 11 12 numChannels select (auto) 13 knob,cv 14 15 16 input: 17 0123456789abcdef 18 19 want: 20 3456 21 22 rotate 4,clip 4 23 24 */ 25 26 27 struct ComputerscareTolyPoolsV2 : Module { 28 int counter = 83910; 29 int numOutputChannelsControlValue = 0; 30 int numOutputChannels = 1; 31 int rotation = 0; 32 33 int knobRotation=0; 34 int numChannelsKnob = 0; 35 36 int numInputChannels = 1; 37 38 int rotationModeEnum=0; 39 int rotationBase = 16; 40 41 ComputerscareSVGPanel* panelRef; 42 enum ParamIds { 43 ROTATE_KNOB, 44 NUM_CHANNELS_KNOB, 45 AUTO_CHANNELS_SWITCH, 46 NUM_PARAMS 47 48 }; 49 enum InputIds { 50 POLY_INPUT, 51 ROTATE_CV, 52 NUM_CHANNELS_CV, 53 NUM_INPUTS 54 }; 55 enum OutputIds { 56 POLY_OUTPUT, 57 NUM_CHANNELS_OUTPUT, 58 NUM_OUTPUTS 59 }; 60 enum LightIds { 61 NUM_LIGHTS 62 }; 63 64 65 ComputerscareTolyPoolsV2() { 66 67 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 68 69 configParam(ROTATE_KNOB, -16.f, 16.f, 0.f, "Rotation Offset", " channels"); 70 71 configParam<AutoParamQuantity>(NUM_CHANNELS_KNOB, 0.f, 16.f, 0.f, "Number of Output Channels Offset"); 72 73 configInput(POLY_INPUT, "Main"); 74 configInput(ROTATE_CV, "Rotation CV"); 75 configInput(NUM_CHANNELS_CV, "Number of Channels CV"); 76 77 configOutput(POLY_OUTPUT, "Main"); 78 configOutput(NUM_CHANNELS_OUTPUT, "Number of Input Channels"); 79 } 80 void process(const ProcessArgs &args) override { 81 counter++; 82 83 int cvRotation = 0; 84 int cvOutputChannels = 0; 85 86 int finalPositiveRotation = 0; 87 88 bool inputIsConnected = inputs[POLY_INPUT].isConnected(); 89 90 if (counter > 982) { 91 counter = 0; 92 numChannelsKnob = params[NUM_CHANNELS_KNOB].getValue(); 93 knobRotation = (int) round(params[ROTATE_KNOB].getValue()); 94 } 95 96 if(inputIsConnected) { 97 numInputChannels = inputs[POLY_INPUT].getChannels(); 98 } else { 99 numInputChannels = 0; 100 } 101 102 if (inputs[NUM_CHANNELS_CV].isConnected()) { 103 cvOutputChannels = (int) round(inputs[NUM_CHANNELS_CV].getVoltage(0)*1.6f); 104 } 105 if (inputs[ROTATE_CV].isConnected()) { 106 cvRotation = (int) round(inputs[ROTATE_CV].getVoltage(0) * 1.6f); 107 } 108 109 rotation = knobRotation+cvRotation; 110 111 numOutputChannelsControlValue = math::clamp(cvOutputChannels+numChannelsKnob,0,16); 112 113 114 if(numOutputChannelsControlValue == 0) { 115 numOutputChannels = inputIsConnected ? numInputChannels : 1; 116 } else { 117 numOutputChannels = numOutputChannelsControlValue; 118 } 119 120 outputs[POLY_OUTPUT].setChannels(numOutputChannels); 121 outputs[NUM_CHANNELS_OUTPUT].setVoltage(mapChannelCountToVoltage(numInputChannels)); 122 123 124 125 if(rotationModeEnum == 0) { 126 rotationBase = inputIsConnected ? numInputChannels : 16; // so when unconnected, the rotation knob illustrates the normal range 127 } else if(rotationModeEnum == 1) { 128 rotationBase = std::max(numOutputChannels,numInputChannels); 129 } else if(rotationModeEnum == 2) { 130 rotationBase = 16; 131 } 132 133 if(rotation > 0) { 134 finalPositiveRotation = rotation % rotationBase; 135 } else if(rotation < 0) { 136 /* 137 eg: rotationBase=16, rotation = -21 138 positiveRotation = 16-(21 % 16) = 16 - (5) = +11 139 */ 140 finalPositiveRotation = rotationBase - ((-rotation) % rotationBase); 141 } 142 143 144 if(inputs[POLY_INPUT].isConnected() && outputs[POLY_OUTPUT].isConnected()) { 145 for (int i = 0; i < numOutputChannels; i++) { 146 outputs[POLY_OUTPUT].setVoltage(inputs[POLY_INPUT].getVoltage((i + finalPositiveRotation) % rotationBase), i); 147 } 148 } else { 149 for (int i = 0; i < numOutputChannels; i++) { 150 outputs[POLY_OUTPUT].setVoltage(0.f, i); 151 } 152 } 153 } 154 155 json_t *dataToJson() override { 156 json_t *rootJ = json_object(); 157 json_object_set_new(rootJ, "rotationModeEnum", json_integer(rotationModeEnum)); 158 return rootJ; 159 } 160 161 void dataFromJson(json_t *rootJ) override { 162 json_t *rotationModeJ = json_object_get(rootJ, "rotationModeEnum"); 163 164 if (rotationModeJ) { 165 rotationModeEnum = json_integer_value(rotationModeJ); 166 } 167 } 168 169 }; 170 struct PoolsSmallDisplayV2 : SmallLetterDisplay 171 { 172 ComputerscareTolyPoolsV2 *module; 173 int ch; 174 int type = 0; 175 PoolsSmallDisplayV2(int someType) 176 { 177 type = someType; 178 SmallLetterDisplay(); 179 }; 180 void draw(const DrawArgs &args) 181 { 182 //this->setNumDivisionsString(); 183 if (module) 184 { 185 186 if (type == 0) { 187 if(module->numOutputChannelsControlValue == 0) { 188 value = "A"; //Automatic - output channels match input channels 189 } else { 190 value = std::to_string(module->numOutputChannelsControlValue); 191 } 192 193 } 194 else if (type == 1) { 195 196 //keep the displayed knob value between -15 and +15 197 int rotationDisplay = 0; 198 if(module->rotation > 0) { 199 rotationDisplay = module->rotation % module->rotationBase; 200 } else if(module->rotation < 0) { 201 rotationDisplay = -1*( (-1* module->rotation) % module->rotationBase); 202 } 203 204 value = std::to_string(rotationDisplay); 205 } 206 else if (type == 2) { 207 value = std::to_string(module->numInputChannels); 208 } 209 210 } 211 else { 212 value = std::to_string((random::u32() % 16) + 1); 213 } 214 SmallLetterDisplay::draw(args); 215 } 216 217 }; 218 219 struct ComputerscareTolyPoolsWidgetV2 : ModuleWidget { 220 ComputerscareTolyPoolsWidgetV2(ComputerscareTolyPoolsV2 *module) { 221 222 setModule(module); 223 box.size = Vec(4 * 15, 380); 224 { 225 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 226 panel->box.size = box.size; 227 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareTolyPoolsPanel.svg"))); 228 229 addChild(panel); 230 231 } 232 233 234 addInput(createInput<InPort>(Vec(1 , 50), module, ComputerscareTolyPoolsV2::POLY_INPUT)); 235 poolsSmallDisplay = new PoolsSmallDisplayV2(2); 236 poolsSmallDisplay->box.size = Vec(14, 20); 237 poolsSmallDisplay->box.pos = Vec(-3 , 80); 238 poolsSmallDisplay->fontSize = 22; 239 poolsSmallDisplay->textAlign = 18; 240 poolsSmallDisplay->breakRowWidth = 20; 241 poolsSmallDisplay->module = module; 242 addChild(poolsSmallDisplay); 243 244 245 addLabeledKnob("Num Output Channels", 10, 156, module, ComputerscareTolyPoolsV2::NUM_CHANNELS_KNOB, -14, -24, 0); 246 addInput(createInput<InPort>(Vec(10, 186), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_CV)); 247 248 addLabeledKnob("Rotation", 10, 256, module, ComputerscareTolyPoolsV2::ROTATE_KNOB, -13, -5, 1); 249 addInput(createInput<InPort>(Vec(10, 286), module, ComputerscareTolyPoolsV2::ROTATE_CV)); 250 251 252 addOutput(createOutput<OutPort>(Vec(28, 30), module, ComputerscareTolyPoolsV2::POLY_OUTPUT)); 253 254 addOutput(createOutput<PointingUpPentagonPort>(Vec(31, 76), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_OUTPUT)); 255 } 256 void addLabeledKnob(std::string label, int x, int y, ComputerscareTolyPoolsV2 *module, int index, float labelDx, float labelDy, int type) { 257 258 poolsSmallDisplay = new PoolsSmallDisplayV2(type); 259 poolsSmallDisplay->box.size = Vec(30, 20); 260 poolsSmallDisplay->box.pos = Vec(x - 7.5 , y + 1.f); 261 poolsSmallDisplay->fontSize = 22; 262 poolsSmallDisplay->textAlign = 18; 263 poolsSmallDisplay->textColor = COLOR_COMPUTERSCARE_LIGHT_GREEN; 264 poolsSmallDisplay->breakRowWidth = 30; 265 poolsSmallDisplay->module = module; 266 267 268 outputChannelLabel = new SmallLetterDisplay(); 269 outputChannelLabel->box.size = Vec(5, 5); 270 outputChannelLabel->box.pos = Vec(x + labelDx, y - 12 + labelDy); 271 outputChannelLabel->fontSize = 15; 272 outputChannelLabel->textAlign = 1; 273 outputChannelLabel->breakRowWidth = 55; 274 275 outputChannelLabel->value = label; 276 277 addParam(createParam<MediumDotSnapKnob>(Vec(x, y), module, index)); 278 addChild(poolsSmallDisplay); 279 280 } 281 282 void appendContextMenu(Menu *menu) override; 283 284 285 PoolsSmallDisplayV2* poolsSmallDisplay; 286 SmallLetterDisplay* outputChannelLabel; 287 }; 288 struct PoolsModeItem : MenuItem { 289 ComputerscareTolyPoolsV2 *pools; 290 int modeEnum; 291 void onAction(const event::Action &e) override { 292 pools->rotationModeEnum = modeEnum; 293 } 294 void step() override { 295 rightText = CHECKMARK(pools->rotationModeEnum == modeEnum); 296 MenuItem::step(); 297 } 298 }; 299 300 void ComputerscareTolyPoolsWidgetV2::appendContextMenu(Menu* menu) { 301 ComputerscareTolyPoolsV2* pools = dynamic_cast<ComputerscareTolyPoolsV2*>(this->module); 302 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 303 304 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Rotation Mode")); 305 menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Repeat Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 0)); 306 menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through Maximum of Output, Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 1)); 307 menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through 16 Channels (Legacy)", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 2)); 308 309 } 310 311 312 Model *modelComputerscareTolyPoolsV2 = createModel<ComputerscareTolyPoolsV2, ComputerscareTolyPoolsWidgetV2>("computerscare-toly-pools-v2");