ComputerscareBolyPuttons.cpp (10638B)
1 #include "Computerscare.hpp" 2 3 struct ComputerscareBolyPuttons; 4 5 const int numToggles = 16; 6 7 struct ComputerscareBolyPuttons : ComputerscarePolyModule { 8 int outputRangeEnum = 0; 9 bool momentary = false; 10 bool radioMode = false; 11 float outputRanges[6][2]; 12 float previousToggle[16] = {0.f}; 13 rack::dsp::SchmittTrigger momentaryTriggers[16]; 14 rack::dsp::PulseGenerator pulseGen[16]; 15 16 ComputerscareSVGPanel* panelRef; 17 enum ParamIds { 18 TOGGLE, 19 POLY_CHANNELS = TOGGLE + numToggles, 20 MOMENTARY_MODE, 21 NUM_PARAMS 22 23 }; 24 enum InputIds { 25 CHANNEL_INPUT, 26 A_INPUT, 27 B_INPUT, 28 NUM_INPUTS 29 }; 30 enum OutputIds { 31 POLY_OUTPUT, 32 NUM_OUTPUTS 33 }; 34 enum LightIds { 35 NUM_LIGHTS 36 }; 37 38 ComputerscareBolyPuttons() { 39 40 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 41 42 for (int i = 0; i < numToggles; i++) { 43 configSwitch(TOGGLE + i, 0.f, 1.f, 0.f, "Channel " + std::to_string(i + 1), {"A", "B"}); 44 } 45 configParam<AutoParamQuantity>(POLY_CHANNELS, 0.f, 16.f, 16.f, "Poly Channels"); 46 47 getParamQuantity(POLY_CHANNELS)->randomizeEnabled = false; 48 getParamQuantity(POLY_CHANNELS)->resetEnabled = false; 49 50 configInput(A_INPUT, "A (Button Up)"); 51 configInput(B_INPUT, "B (Button Down)"); 52 configOutput(POLY_OUTPUT, "Main"); 53 54 outputRanges[0][0] = 0.f; 55 outputRanges[0][1] = 10.f; 56 outputRanges[1][0] = -5.f; 57 outputRanges[1][1] = 5.f; 58 outputRanges[2][0] = 0.f; 59 outputRanges[2][1] = 5.f; 60 outputRanges[3][0] = 0.f; 61 outputRanges[3][1] = 1.f; 62 outputRanges[4][0] = -1.f; 63 outputRanges[4][1] = 1.f; 64 outputRanges[5][0] = -10.f; 65 outputRanges[5][1] = 10.f; 66 } 67 void switchOffAllButtonsButOne(int index) { 68 for (int i = 0; i < numToggles; i++) { 69 if (i != index) { 70 params[TOGGLE + i].setValue(0.f); 71 } 72 } 73 } 74 void checkForParamChanges() { 75 int changeIndex = -1; 76 float val; 77 for (int i = 0; i < numToggles; i++) { 78 val = params[TOGGLE + i].getValue(); 79 if (val == 1.f && previousToggle[i] != val) { 80 changeIndex = i; 81 } 82 previousToggle[i] = val; 83 } 84 if (changeIndex > -1) { 85 switchOffAllButtonsButOne(changeIndex); 86 } 87 } 88 void legacyJSON(json_t *rootJ) { 89 json_t *outputRangeEnumJ = json_object_get(rootJ, "outputRange"); 90 if (outputRangeEnumJ) { outputRangeEnum = json_integer_value(outputRangeEnumJ); } 91 json_t *radioModeJ = json_object_get(rootJ, "radioMode"); 92 if (radioModeJ) { radioMode = json_is_true(radioModeJ); } 93 json_t *momentaryModeJ = json_object_get(rootJ, "momentaryMode"); 94 if (momentaryModeJ) { momentary = json_is_true(momentaryModeJ); } 95 } 96 void dataFromJson(json_t *rootJ) override { 97 legacyJSON(rootJ); 98 } 99 json_t *dataToJson() override 100 { 101 json_t *rootJ = json_object(); 102 json_object_set_new(rootJ, "outputRange", json_integer(outputRangeEnum)); 103 json_object_set_new(rootJ, "radioMode", json_boolean(radioMode)); 104 json_object_set_new(rootJ, "momentaryMode", json_boolean(momentary)); 105 return rootJ; 106 } 107 108 void onRandomize() override { 109 if (momentary) { 110 } else { 111 if (radioMode) { 112 int rIndex = floor(random::uniform() * 16); 113 switchOffAllButtonsButOne(rIndex); 114 params[TOGGLE + rIndex].setValue(1.f); 115 } 116 else { 117 for (int i = 0; i < numToggles; i++) { 118 params[TOGGLE + i].setValue(random::uniform() < 0.5 ? 0.f : 1.f); 119 } 120 } 121 } 122 } 123 void checkPoly() override { 124 int aChannels = inputs[A_INPUT].getChannels(); 125 int bChannels = inputs[B_INPUT].getChannels(); 126 int knobSetting = params[POLY_CHANNELS].getValue(); 127 if (knobSetting == 0) { 128 polyChannels = (aChannels <= 1 && bChannels <= 1) ? 16 : std::max(aChannels, bChannels); 129 } 130 else { 131 polyChannels = knobSetting; 132 } 133 outputs[POLY_OUTPUT].setChannels(polyChannels); 134 } 135 void toggleMomentary() { 136 momentary = !momentary; 137 if (momentary) { 138 switchOffAllButtonsButOne(-1); 139 } 140 } 141 void toggleRadio() { 142 radioMode = !radioMode; 143 if (radioMode) { 144 switchOffAllButtonsButOne(-1); 145 } 146 } 147 void process(const ProcessArgs &args) override { 148 ComputerscarePolyModule::checkCounter(); 149 150 float rangeMin = outputRanges[outputRangeEnum][0]; 151 float rangeMax = outputRanges[outputRangeEnum][1]; 152 int numAChannels = inputs[A_INPUT].getChannels(); 153 int numBChannels = inputs[B_INPUT].getChannels(); 154 155 float min; 156 float max; 157 float spread; 158 159 160 if (radioMode && !momentary) { 161 checkForParamChanges(); 162 } 163 164 if (numAChannels == 1) { 165 min = inputs[A_INPUT].getVoltage(0); 166 } 167 if (numBChannels == 1) { 168 max = inputs[B_INPUT].getVoltage(0); 169 } 170 for (int i = 0; i < polyChannels; i++) { 171 if (numAChannels != 1) { 172 min = i < numAChannels ? inputs[A_INPUT].getVoltage(i) : rangeMin; 173 } 174 if (numBChannels != 1) { 175 max = i < numBChannels ? inputs[B_INPUT].getVoltage(i) : rangeMax; 176 } 177 spread = max - min; 178 outputs[POLY_OUTPUT].setVoltage(params[TOGGLE + i].getValue()*spread + min, i); 179 } 180 } 181 182 }; 183 184 struct DisableableParamWidget : SmallIsoButton { 185 ComputerscareBolyPuttons *module; 186 SmallLetterDisplay *smallLetterDisplay; 187 int channel; 188 Vec labelOffset = Vec(0, 0); 189 bool pressed = false; 190 191 192 DisableableParamWidget() { 193 smallLetterDisplay = new SmallLetterDisplay(); 194 smallLetterDisplay->box.size = Vec(5, 10); 195 smallLetterDisplay->fontSize = 17; 196 smallLetterDisplay->value = ""; 197 smallLetterDisplay->textAlign = 1; 198 smallLetterDisplay->box.pos = box.pos; 199 200 addChild(smallLetterDisplay); 201 SmallIsoButton(); 202 } 203 void step() override { 204 if (module) { 205 disabled = channel > module->polyChannels - 1; 206 momentary = module->momentary; 207 pressed = module->params[channel].getValue() == 1.f; 208 } 209 else { 210 disabled = false; 211 } 212 SmallIsoButton::step(); 213 } 214 void draw(const DrawArgs &ctx) override { 215 labelOffset = Vec(pressed ? 3.f : -4.f, pressed ? 7.f : 2.f); 216 smallLetterDisplay->value = std::to_string(channel + 1); 217 218 smallLetterDisplay->textOffset = labelOffset; 219 SmallIsoButton::draw(ctx); 220 } 221 }; 222 223 struct ComputerscareBolyPuttonsWidget : ModuleWidget { 224 ComputerscareBolyPuttonsWidget(ComputerscareBolyPuttons *module) { 225 226 setModule(module); 227 box.size = Vec(4 * 15, 380); 228 { 229 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 230 panel->box.size = box.size; 231 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareBolyPuttonsPanel.svg"))); 232 addChild(panel); 233 234 } 235 236 channelWidget = new PolyOutputChannelsWidget(Vec(22, 23), module, ComputerscareBolyPuttons::POLY_CHANNELS); 237 addChild(channelWidget); 238 239 float xx; 240 float yy; 241 float dx; 242 float dy; 243 for (int i = 0; i < numToggles; i++) { 244 xx = 5.2f + 27.3 * (i - i % 8) / 8; 245 yy = 92 + 33.5 * (i % 8) + 14.3 * (i - i % 8) / 8; 246 dx = 4 - (i - i % 8) * 0.9; 247 dy = 19; 248 addLabeledButton(std::to_string(i + 1), xx, yy, module, i, dx, dy); 249 } 250 251 addInput(createInput<InPort>(Vec(9, 58), module, ComputerscareBolyPuttons::A_INPUT)); 252 addInput(createInput<PointingUpPentagonPort>(Vec(33, 55), module, ComputerscareBolyPuttons::B_INPUT)); 253 254 addOutput(createOutput<PointingUpPentagonPort>(Vec(1, 24), module, ComputerscareBolyPuttons::POLY_OUTPUT)); 255 bolyPuttons = module; 256 } 257 void addLabeledButton(std::string label, int x, int y, ComputerscareBolyPuttons *module, int index, float labelDx, float labelDy) { 258 259 DisableableParamWidget* button = createParam<DisableableParamWidget>(Vec(x, y), module, ComputerscareBolyPuttons::TOGGLE + index); 260 261 button->module = module; 262 button->channel = index; 263 addParam(button); 264 } 265 266 /*void fromJson(json_t *rootJ) override 267 { 268 ModuleWidget::fromJson(rootJ); 269 bolyPuttons->legacyJSON(rootJ); 270 }*/ 271 void appendContextMenu(Menu *menu) override; 272 273 DisableableParamWidget* button; 274 PolyOutputChannelsWidget* channelWidget; 275 ComputerscareBolyPuttons *bolyPuttons; 276 SmallLetterDisplay* smallLetterDisplay; 277 }; 278 struct OutputRangeItem : MenuItem { 279 ComputerscareBolyPuttons *bolyPuttons; 280 int outputRangeEnum; 281 void onAction(const event::Action &e) override { 282 bolyPuttons->outputRangeEnum = outputRangeEnum; 283 } 284 void step() override { 285 rightText = CHECKMARK(bolyPuttons->outputRangeEnum == outputRangeEnum); 286 MenuItem::step(); 287 } 288 }; 289 struct RadioModeMenuItem: MenuItem { 290 ComputerscareBolyPuttons *bolyPuttons; 291 RadioModeMenuItem() { 292 293 } 294 void onAction(const event::Action &e) override { 295 bolyPuttons->toggleRadio(); 296 } 297 void step() override { 298 rightText = bolyPuttons->radioMode ? "✔" : ""; 299 MenuItem::step(); 300 } 301 }; 302 struct MomentaryModeMenuItem: MenuItem { 303 ComputerscareBolyPuttons *bolyPuttons; 304 MomentaryModeMenuItem() { 305 306 } 307 void onAction(const event::Action &e) override { 308 bolyPuttons->toggleMomentary(); 309 } 310 void step() override { 311 rightText = bolyPuttons->momentary ? "✔" : ""; 312 MenuItem::step(); 313 } 314 }; 315 316 317 void ComputerscareBolyPuttonsWidget::appendContextMenu(Menu *menu) 318 { 319 ComputerscareBolyPuttons *bolyPuttons = dynamic_cast<ComputerscareBolyPuttons *>(this->module); 320 321 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 322 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "How The Buttons Work")); 323 RadioModeMenuItem *radioMode = new RadioModeMenuItem(); 324 radioMode->text = "Exclusive Mode (like radio buttons: only can be pressed at a time)"; 325 radioMode->bolyPuttons = bolyPuttons; 326 menu->addChild(radioMode); 327 328 329 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 330 MomentaryModeMenuItem *momentaryMode = new MomentaryModeMenuItem(); 331 momentaryMode->text = "Momentary (gate output while button is held)"; 332 momentaryMode->bolyPuttons = bolyPuttons; 333 menu->addChild(momentaryMode); 334 335 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 336 337 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Off / On Values (A ... B)")); 338 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " 0v ... +10v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 0)); 339 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " -5v ... +5v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 1)); 340 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " 0v ... +5v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 2)); 341 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " 0v ... +1v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 3)); 342 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " -1v ... +1v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 4)); 343 menu->addChild(construct<OutputRangeItem>(&MenuItem::text, "-10v ... +10v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 5)); 344 345 346 347 348 } 349 Model *modelComputerscareBolyPuttons = createModel<ComputerscareBolyPuttons, ComputerscareBolyPuttonsWidget>("computerscare-bolyputtons");