ComputerscareRolyPouter.cpp (9790B)
1 #include "Computerscare.hpp" 2 #include "dtpulse.hpp" 3 4 struct ComputerscareRolyPouter; 5 6 const int numKnobs = 16; 7 8 struct ComputerscareRolyPouter : ComputerscarePolyModule { 9 int counter = 0; 10 int routing[numKnobs]; 11 int numOutputChannels = 16; 12 int numInputChannels = -1; 13 ComputerscareSVGPanel* panelRef; 14 enum ParamIds { 15 KNOB, 16 POLY_CHANNELS = KNOB + numKnobs, 17 RANDOMIZE_ONE_TO_ONE, 18 NUM_PARAMS 19 }; 20 enum InputIds { 21 POLY_INPUT, 22 ROUTING_CV, 23 NUM_INPUTS 24 }; 25 enum OutputIds { 26 POLY_OUTPUT, 27 NUM_OUTPUTS 28 }; 29 enum LightIds { 30 NUM_LIGHTS 31 }; 32 33 34 ComputerscareRolyPouter() { 35 36 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 37 38 for (int i = 0; i < numKnobs; i++) { 39 configParam(KNOB + i, 1.f, 16.f, (i + 1), "output ch" + std::to_string(i + 1) + " = input ch"); 40 routing[i] = i; 41 } 42 configParam<AutoParamQuantity>(POLY_CHANNELS, 0.f, 16.f, 16.f, "Poly Channels"); 43 configParam(RANDOMIZE_ONE_TO_ONE, 0.f, 1.f, 0.f); 44 45 getParamQuantity(POLY_CHANNELS)->randomizeEnabled = false; 46 getParamQuantity(POLY_CHANNELS)->resetEnabled = false; 47 getParamQuantity(RANDOMIZE_ONE_TO_ONE)->randomizeEnabled = false; 48 49 configInput(POLY_INPUT, "Main"); 50 configInput(ROUTING_CV, "Routing CV"); 51 52 configOutput(POLY_OUTPUT, "Re-Routed"); 53 54 } 55 void setAll(int setVal) { 56 for (int i = 0; i < 16; i++) { 57 params[KNOB + i].setValue(setVal); 58 } 59 } 60 void onRandomize() override { 61 float max = numInputChannels > 0 ? numInputChannels : 16; 62 if (params[RANDOMIZE_ONE_TO_ONE].getValue() == 1) { 63 int tempRouting[polyChannels]; 64 for (int i = 0; i < polyChannels; i++) { 65 tempRouting[i] = i + 1; 66 } 67 for (int i = polyChannels - 1; i > 0; i--) 68 { 69 // Pick a random index from 0 to i 70 int j = ((int) std::floor(random::uniform() * 1000)) % (i + 1); 71 72 // Swap arr[i] with the element 73 // at random index 74 swap(&tempRouting[i], &tempRouting[j]); 75 } 76 for (int i = 0; i < polyChannels; i++) { 77 params[KNOB + i].setValue(tempRouting[i]); 78 } 79 } 80 else { 81 for (int i = 0; i < polyChannels; i++) { 82 params[KNOB + i].setValue(1 + std::floor(random::uniform()*max)); 83 } 84 } 85 } 86 void toggleOneToOne() { 87 int prev = params[RANDOMIZE_ONE_TO_ONE].getValue(); 88 params[RANDOMIZE_ONE_TO_ONE].setValue(prev == 1 ? 0 : 1); 89 } 90 void checkPoly() override { 91 numInputChannels = inputs[POLY_INPUT].getChannels(); 92 int cvChannels = inputs[ROUTING_CV].getChannels(); 93 int knobSetting = params[POLY_CHANNELS].getValue(); 94 if (numInputChannels > 0) { 95 if (knobSetting == 0) { 96 polyChannels = numInputChannels; 97 } 98 else { 99 polyChannels = knobSetting; 100 } 101 } else { 102 polyChannels = knobSetting == 0 ? 16 : knobSetting; 103 } 104 outputs[POLY_OUTPUT].setChannels(polyChannels); 105 } 106 void process(const ProcessArgs &args) override { 107 ComputerscarePolyModule::checkCounter(); 108 counter++; 109 int inputChannels = inputs[POLY_INPUT].getChannels(); 110 int cvChannels = inputs[ROUTING_CV].getChannels(); 111 int knobSetting; 112 113 //outputs[POLY_OUTPUT].setChannels(numOutputChannels); 114 115 //if() 116 if (cvChannels > 0) { 117 for (int i = 0; i < numOutputChannels; i++) { 118 119 knobSetting = std::round(inputs[ROUTING_CV].getVoltage(cvChannels == 1 ? 0 : i) * 1.5) + 1; 120 routing[i] = (knobSetting + 16 * 4 - 1) % 16; 121 if (routing[i] > inputChannels) { 122 outputs[POLY_OUTPUT].setVoltage(0, i); 123 } 124 else { 125 outputs[POLY_OUTPUT].setVoltage(inputs[POLY_INPUT].getVoltage(routing[i]), i); 126 } 127 } 128 } else { 129 if (counter > 8) { 130 //printf("%f \n",random::uniform()); 131 counter = 0; 132 for (int i = 0; i < numKnobs; i++) { 133 routing[i] = (int)params[KNOB + i].getValue() - 1; 134 } 135 136 } 137 for (int i = 0; i < numOutputChannels; i++) { 138 knobSetting = params[KNOB + i].getValue(); 139 if (knobSetting > inputChannels) { 140 outputs[POLY_OUTPUT].setVoltage(0, i); 141 } 142 else { 143 outputs[POLY_OUTPUT].setVoltage(inputs[POLY_INPUT].getVoltage(knobSetting - 1), i); 144 } 145 } 146 } 147 } 148 149 }; 150 struct PouterSmallDisplay : SmallLetterDisplay 151 { 152 ComputerscareRolyPouter *module; 153 int ch; 154 NVGcolor okayColor = COLOR_COMPUTERSCARE_LIGHT_GREEN; 155 NVGcolor outOfBoundsColor = COLOR_COMPUTERSCARE_YELLOW; 156 PouterSmallDisplay(int outputChannelNumber) 157 { 158 ch = outputChannelNumber; 159 SmallLetterDisplay(); 160 }; 161 void draw(const DrawArgs &args) 162 { 163 if (module) 164 { 165 std::string str = std::to_string(module->routing[ch] + 1); 166 if (module->numInputChannels > 0 && (module->routing[ch] > module->numInputChannels)) { 167 textColor = outOfBoundsColor; 168 } 169 else { 170 textColor = okayColor; 171 } 172 value = str; 173 } 174 else { 175 textColor = okayColor; 176 value = std::to_string((random::u32() % 16) + 1); 177 } 178 SmallLetterDisplay::draw(args); 179 } 180 }; 181 struct DisableableSnapKnob : RoundKnob { 182 ComputerscarePolyModule *module; 183 int channel; 184 bool disabled = false; 185 int lastDisabled = -1; 186 std::shared_ptr<Svg> enabledSvg = APP->window->loadSvg(asset::plugin(pluginInstance, "res/computerscare-medium-knob-dot-indicator.svg")); 187 std::shared_ptr<Svg> disabledSvg = APP->window->loadSvg(asset::plugin(pluginInstance, "res/computerscare-medium-knob-dot-indicator-disabled.svg")); 188 189 DisableableSnapKnob() { 190 snap = true; 191 shadow->opacity = 0.f; 192 RoundKnob(); 193 } 194 void step() override { 195 if (module) { 196 disabled = channel > module->polyChannels - 1; 197 } 198 else { 199 disabled = false; 200 setSvg(enabledSvg); 201 onChange(*(new event::Change())); 202 fb->dirty = true; 203 } 204 if (disabled != lastDisabled) { 205 setSvg(disabled ? disabledSvg : enabledSvg); 206 onChange(*(new event::Change())); 207 fb->dirty = true; 208 lastDisabled = disabled; 209 } 210 RoundKnob::step(); 211 } 212 }; 213 struct ComputerscareRolyPouterWidget : ModuleWidget { 214 ComputerscareRolyPouterWidget(ComputerscareRolyPouter *module) { 215 216 setModule(module); 217 box.size = Vec(4 * 15, 380); 218 { 219 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 220 panel->box.size = box.size; 221 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareRolyPouterPanel.svg"))); 222 addChild(panel); 223 224 } 225 channelWidget = new PolyOutputChannelsWidget(Vec(2, 8), module, ComputerscareRolyPouter::POLY_CHANNELS); 226 addChild(channelWidget); 227 228 addInput(createInput<PointingUpPentagonPort>(Vec(22, 52), module, ComputerscareRolyPouter::ROUTING_CV)); 229 230 float xx; 231 float yy; 232 for (int i = 0; i < numKnobs; i++) { 233 xx = 1.4f + 24.3 * (i - i % 8) / 8; 234 yy = 66 + 36.5 * (i % 8) + 14.3 * (i - i % 8) / 8; 235 addLabeledKnob(std::to_string(i + 1), xx, yy, module, i, (i - i % 8) * 1.3 - 5, i < 8 ? 4 : 0); 236 } 237 addInput(createInput<InPort>(Vec(1, 36), module, ComputerscareRolyPouter::POLY_INPUT)); 238 addOutput(createOutput<PointingUpPentagonPort>(Vec(32, 18), module, ComputerscareRolyPouter::POLY_OUTPUT)); 239 240 } 241 void addLabeledKnob(std::string label, int x, int y, ComputerscareRolyPouter *module, int index, float labelDx, float labelDy) { 242 243 pouterSmallDisplay = new PouterSmallDisplay(index); 244 pouterSmallDisplay->box.size = Vec(20, 20); 245 pouterSmallDisplay->box.pos = Vec(x - 2.5 , y + 1.f); 246 pouterSmallDisplay->fontSize = 26; 247 pouterSmallDisplay->textAlign = 18; 248 pouterSmallDisplay->breakRowWidth = 20; 249 pouterSmallDisplay->module = module; 250 251 252 outputChannelLabel = new SmallLetterDisplay(); 253 outputChannelLabel->box.size = Vec(5, 5); 254 outputChannelLabel->box.pos = Vec(x + labelDx, y - 12 + labelDy); 255 outputChannelLabel->fontSize = 14; 256 outputChannelLabel->textAlign = index < 8 ? 1 : 4; 257 outputChannelLabel->breakRowWidth = 15; 258 259 outputChannelLabel->value = std::to_string(index + 1); 260 261 DisableableSnapKnob* knob = createParam<DisableableSnapKnob>(Vec(x, y), module, ComputerscareRolyPouter::KNOB + index); 262 knob->channel = index; 263 knob->module = module; 264 addParam(knob); 265 266 addChild(pouterSmallDisplay); 267 addChild(outputChannelLabel); 268 269 } 270 void appendContextMenu(Menu *menu) override 271 { 272 ComputerscareRolyPouter *module = dynamic_cast<ComputerscareRolyPouter *>(this->module); 273 274 struct ssmi : MenuItem 275 { 276 ComputerscareRolyPouter *pouter; 277 int mySetVal = 1; 278 ssmi(int setVal) 279 { 280 mySetVal = setVal; 281 MenuItem(); 282 } 283 284 void onAction(const event::Action &e) override 285 { 286 pouter->setAll(mySetVal); 287 } 288 }; 289 struct OneToOneItem: MenuItem { 290 ComputerscareRolyPouter *pouter; 291 292 OneToOneItem() { 293 MenuItem(); 294 } 295 void onAction(const event::Action &e) override { 296 pouter->toggleOneToOne(); 297 } 298 void step() override { 299 rightText = pouter->params[ComputerscareRolyPouter::RANDOMIZE_ONE_TO_ONE].getValue() == 1.f ? "✔" : ""; 300 MenuItem::step(); 301 } 302 }; 303 304 struct SetAllItem : MenuItem { 305 ComputerscareRolyPouter *pouter; 306 307 Menu *createChildMenu() override { 308 Menu *menu = new Menu; 309 for (unsigned int i = 1; i < 17; i++) { 310 ssmi *menuItem = new ssmi(i); 311 menuItem->text = "Set all to ch. " + std::to_string(i); 312 menuItem->pouter = pouter; 313 menu->addChild(menuItem); 314 } 315 return menu; 316 } 317 }; 318 319 MenuLabel *spacerLabel = new MenuLabel(); 320 menu->addChild(spacerLabel); 321 322 OneToOneItem *oneToOne = new OneToOneItem(); 323 oneToOne->text = "Randomize one-to-one (Don't re-use input channels on randomize)"; 324 oneToOne->pouter = module; 325 menu->addChild(oneToOne); 326 327 328 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 329 330 SetAllItem *setAllItem = new SetAllItem(); 331 setAllItem->text = "Set All To"; 332 setAllItem->rightText = RIGHT_ARROW; 333 setAllItem->pouter = module; 334 menu->addChild(setAllItem); 335 336 } 337 338 DisableableSnapKnob* knob; 339 PolyOutputChannelsWidget* channelWidget; 340 PouterSmallDisplay* pouterSmallDisplay; 341 SmallLetterDisplay* outputChannelLabel; 342 343 void addMenuItems(ComputerscareRolyPouter *pouter, Menu *menu); 344 }; 345 346 347 348 Model *modelComputerscareRolyPouter = createModel<ComputerscareRolyPouter, ComputerscareRolyPouterWidget>("computerscare-roly-pouter");