ComputerscarePatchSequencer.cpp (23165B)
1 #include "Computerscare.hpp" 2 3 #include <string> 4 #include <sstream> 5 #include <iomanip> 6 7 const int maxSteps = 16; 8 const int numInputs = 10; 9 const int numOutputs = 10; 10 11 struct ComputerscareDebug; 12 13 struct ComputerscarePatchSequencer : Module { 14 enum ParamIds { 15 STEPS_PARAM, 16 MANUAL_CLOCK_PARAM, 17 EDIT_PARAM, 18 EDIT_PREV_PARAM, 19 ENUMS(SWITCHES, 100), 20 RESET_PARAM, 21 NUM_PARAMS 22 }; 23 enum InputIds { 24 TRG_INPUT, 25 ENUMS(INPUT_JACKS, 10), 26 RANDOMIZE_INPUT, 27 RESET_INPUT, 28 NUM_INPUTS 29 }; 30 enum OutputIds { 31 OUTPUTS, 32 NUM_OUTPUTS = OUTPUTS + 10 33 }; 34 enum LightIds { 35 SWITCH_LIGHTS, 36 NUM_LIGHTS = SWITCH_LIGHTS + 200 37 }; 38 39 rack::dsp::SchmittTrigger switch_triggers[10][10]; 40 41 rack::dsp::SchmittTrigger nextAddressRead; 42 rack::dsp::SchmittTrigger nextAddressEdit; 43 rack::dsp::SchmittTrigger prevAddressEdit; 44 rack::dsp::SchmittTrigger clockTrigger; 45 rack::dsp::SchmittTrigger randomizeTrigger; 46 rack::dsp::SchmittTrigger resetTriggerInput; 47 rack::dsp::SchmittTrigger resetTriggerButton; 48 49 int address = 0; 50 int editAddress = 0; 51 int addressPlusOne = 1; 52 int editAddressPlusOne = 1; 53 int counter = 513; 54 55 int numAddresses = 2; 56 bool switch_states[maxSteps][10][10] = {}; 57 58 bool onlyRandomizeActive = true; 59 60 float input_values[numInputs * 16] = {0.0}; 61 float sums[numOutputs * 16] = {0.0}; 62 63 int randomizationStepEnum = 0; //0: edit step, 1: active step, 2: all steps 64 int randomizationOutputBoundsEnum = 1; //0: randomize exactly one per output, 1: randomize exactly one per output, 2: randomize 1 or more, 3: randomize 0 or more 65 66 int channelCount[numOutputs]; 67 int channelCountEnum = -1; 68 69 ComputerscarePatchSequencer() { 70 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 71 configParam(STEPS_PARAM, 1.f, 16.f, 2.0f, "Number of Steps"); 72 for (int i = 0; i < numOutputs; i++) { 73 channelCount[i] = 0; 74 configInput(INPUT_JACKS + i, "Row " + std::to_string(i + 1)); 75 configOutput(OUTPUTS + i, "Column " + std::to_string(i + 1)); 76 } 77 78 for (int inRow = 0; inRow < numInputs; inRow++) { 79 for (int outCol = 0; outCol < numOutputs; outCol++) { 80 configButton(SWITCHES + outCol * numInputs + inRow, "Toggle Input Row " + std::to_string(inRow + 1) + ",Output Column " + std::to_string(outCol + 1)); 81 } 82 } 83 getParamQuantity(STEPS_PARAM)->randomizeEnabled = false; 84 85 configButton(MANUAL_CLOCK_PARAM, "Manual Scene Advance"); 86 configButton(RESET_PARAM, "Reset To Scene 1"); 87 88 configButton(EDIT_PARAM, "Edit Next Scene"); 89 configButton(EDIT_PREV_PARAM, "Edit Previous Scene"); 90 91 configInput(TRG_INPUT, "Clock"); 92 configInput(RESET_INPUT, "Reset Trigger"); 93 configInput(RANDOMIZE_INPUT, "Randomize Trigger"); 94 95 } 96 void process(const ProcessArgs &args) override; 97 98 99 void updateChannelCount() { 100 int currentMax; 101 102 for (int j = 0; j < numOutputs; j++) { 103 if (channelCountEnum == -1) { 104 currentMax = 0; 105 for (int i = 0; i < numInputs; i++) { 106 if (switch_states[address][i][j] && inputs[INPUT_JACKS + i].isConnected()) { 107 currentMax = std::max(currentMax, inputs[INPUT_JACKS + i].getChannels()); 108 } 109 } 110 } 111 else { 112 currentMax = channelCountEnum; 113 } 114 channelCount[j] = currentMax; 115 outputs[OUTPUTS + j].setChannels(currentMax); 116 } 117 } 118 119 int getRandomizationStepEnum() { 120 return randomizationStepEnum; 121 } 122 123 int getRandomizationOutputBoundsEnum() { 124 return randomizationOutputBoundsEnum; 125 } 126 127 void setRandomizationStepEnum(int randomizationStep) { 128 randomizationStepEnum = randomizationStep; 129 } 130 void setRandomizationOutputBoundsEnum(int randomizationOutputBounds) { 131 randomizationOutputBoundsEnum = randomizationOutputBounds; 132 } 133 134 void onRandomize() override { 135 randomizePatchMatrix(); 136 } 137 138 void randomizePatchMatrix() 139 { 140 if (onlyRandomizeActive) { 141 randomizeMatrixOnlyActive(); 142 } 143 else { 144 randomizeMatrixIncludingDisconnected(); 145 } 146 }; 147 148 149 // For more advanced Module features, read Rack's engine.hpp header file 150 // - toJson, fromJson: serialization of internal data 151 // - onSampleRateChange: event triggered by a change of sample rate 152 // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu 153 154 void randomizeMatrixOnlyActive() { 155 int randomIndex; 156 157 bool connectedInputs[10]; 158 bool connectedOutputs[10]; 159 int numConnectedInputs = 0; 160 161 std::vector<int> connectedInputIndices; 162 163 for (int i = 0; i < 10; i++) 164 { 165 if (inputs[INPUT_JACKS + i].isConnected()) { 166 numConnectedInputs++; 167 connectedInputIndices.push_back(i); 168 } 169 170 connectedInputs[i] = inputs[INPUT_JACKS + i].isConnected(); 171 connectedOutputs[i] = outputs[OUTPUTS + i].isConnected(); 172 } 173 for (int k = 0; k < maxSteps; k++) { 174 if ((randomizationStepEnum == 0 && k == editAddress) || (randomizationStepEnum == 1 && k == address) || randomizationStepEnum == 2) { 175 for (int i = 0; i < 10; i++) { 176 randomIndex = numConnectedInputs > 0 ? connectedInputIndices[floor(random::uniform() * numConnectedInputs)] : 0; 177 if (connectedOutputs[i]) { 178 for (int j = 0; j < 10; j++) { 179 if (j == randomIndex) 180 switch_states[k][j][i] = 1; 181 else 182 switch_states[k][j][i] = 0; 183 } 184 } 185 } 186 } 187 } 188 189 } 190 191 void randomizeMatrixIncludingDisconnected() { 192 int randomIndex; 193 for (int k = 0; k < maxSteps; k++) { 194 if ((randomizationStepEnum == 0 && k == editAddress) || (randomizationStepEnum == 1 && k == address) || randomizationStepEnum == 2) { 195 for (int i = 0; i < 10; i++) 196 { 197 randomIndex = floor(random::uniform() * 10); 198 199 for (int j = 0; j < 10; j++) 200 { 201 if (randomizationOutputBoundsEnum == 3) { 202 switch_states[k][j][i] = (j == randomIndex || random::uniform() < 0.2) ? 1 : 0; 203 } 204 else if (randomizationOutputBoundsEnum == 2) { 205 switch_states[k][j][i] = random::uniform() < 0.2 ? 1 : 0; 206 } 207 else if (randomizationOutputBoundsEnum == 0) { 208 switch_states[k][j][i] = (j == randomIndex && random::uniform() < 0.7) ? 1 : 0; 209 } 210 else { 211 switch_states[k][j][i] = j == randomIndex ? 1 : 0; 212 } 213 } 214 } 215 } 216 } 217 218 } 219 220 void onReset() override 221 { 222 for (int k = 0; k < maxSteps; k++) { 223 224 225 for (int i = 0; i < 10; i++) 226 { 227 for (int j = 0; j < 10; j++) 228 { 229 switch_states[k][i][j] = 0; 230 } 231 } 232 } 233 }; // end randomize() 234 235 236 void dataFromJson(json_t *rootJ) override { 237 // button states 238 json_t *button_statesJ = json_object_get(rootJ, "buttons"); 239 if (button_statesJ) 240 { 241 for (int k = 0; k < maxSteps; k++) { 242 243 for (int i = 0; i < 10; i++) { 244 for (int j = 0; j < 10; j++) { 245 json_t *button_stateJ = json_array_get(button_statesJ, k * 100 + i * 10 + j); 246 if (button_stateJ) 247 switch_states[k][i][j] = !!json_integer_value(button_stateJ); 248 } 249 } 250 } 251 } 252 json_t *onlyRandomizeActiveJ = json_object_get(rootJ, "onlyRandomizeActive"); 253 if (onlyRandomizeActiveJ) { onlyRandomizeActive = json_is_true(onlyRandomizeActiveJ); } 254 255 json_t *randomizationStepEnumJ = json_object_get(rootJ, "randomizationStepEnum"); 256 if (randomizationStepEnumJ) { setRandomizationStepEnum(json_integer_value(randomizationStepEnumJ)); } 257 258 json_t *channelCountEnumJ = json_object_get(rootJ, "channelCountEnum"); 259 if (channelCountEnumJ) { channelCountEnum = json_integer_value(channelCountEnumJ); } 260 261 json_t *randomizationOutputBoundsEnumJ = json_object_get(rootJ, "randomizationOutputBoundsEnum"); 262 if (randomizationOutputBoundsEnumJ) { setRandomizationOutputBoundsEnum(json_integer_value(randomizationOutputBoundsEnumJ)); } 263 264 } 265 json_t *dataToJson() override 266 { 267 268 json_t *rootJ = json_object(); 269 // button states 270 json_t *button_statesJ = json_array(); 271 for (int k = 0; k < maxSteps; k++) { 272 for (int i = 0; i < 10; i++) 273 { 274 for (int j = 0; j < 10; j++) 275 { 276 json_t *button_stateJ = json_integer((int) switch_states[k][i][j]); 277 json_array_append_new(button_statesJ, button_stateJ); 278 } 279 } 280 } 281 json_object_set_new(rootJ, "buttons", button_statesJ); 282 json_object_set_new(rootJ, "onlyRandomizeActive", json_boolean(onlyRandomizeActive)); 283 json_object_set_new(rootJ, "channelCountEnum", json_integer(channelCountEnum)); 284 json_object_set_new(rootJ, "randomizationStepEnum", json_integer(getRandomizationStepEnum())); 285 json_object_set_new(rootJ, "randomizationOutputBoundsEnum", json_integer(getRandomizationOutputBoundsEnum())); 286 return rootJ; 287 } 288 }; 289 290 291 void ComputerscarePatchSequencer::process(const ProcessArgs &args) { 292 293 int numStepsKnobPosition = (int) clamp(roundf(params[STEPS_PARAM].getValue()), 1.0f, 16.0f); 294 //int channels[10] = {0}; 295 296 for ( int j = 0 ; j < 10 ; j++) 297 { 298 //channels[i] = inputs[INPUT_JACKS + i].getChannels(); 299 for (int c = 0; c < 16; c++) { 300 sums[j * 16 + c] = 0.0; 301 } 302 303 304 } 305 306 for (int i = 0 ; i < 10 ; i++) 307 { 308 for (int j = 0 ; j < 10 ; j++) 309 { 310 if (switch_triggers[i][j].process(params[SWITCHES + j * 10 + i].getValue())) 311 { 312 // handle button clicks in the patch matrix 313 switch_states[editAddress][i][j] = !switch_states[editAddress][i][j]; 314 } 315 316 317 } 318 } 319 if (counter > 512) { 320 updateChannelCount(); 321 for (int i = 0 ; i < 10 ; i++) 322 { 323 for (int j = 0 ; j < 10 ; j++) 324 { 325 326 // update the green lights (step you are editing) and the red lights (current active step) 327 lights[SWITCH_LIGHTS + i + j * 10].value = (switch_states[editAddress][i][j]) ? 1.0 : 0.0; 328 lights[SWITCH_LIGHTS + i + j * 10 + 100].value = (switch_states[address][i][j]) ? 1.0 : 0.0; 329 } 330 } 331 counter = 0; 332 } 333 counter++; 334 335 336 if (numStepsKnobPosition != numAddresses) { 337 numAddresses = numStepsKnobPosition; 338 } 339 340 if (randomizeTrigger.process(inputs[RANDOMIZE_INPUT].getVoltage() / 2.f)) { 341 randomizePatchMatrix(); 342 } 343 if (nextAddressEdit.process(params[EDIT_PARAM].getValue()) ) { 344 editAddress = editAddress + 1; 345 editAddress = editAddress % maxSteps; 346 } 347 if (prevAddressEdit.process(params[EDIT_PREV_PARAM].getValue()) ) { 348 editAddress = editAddress - 1; 349 editAddress = editAddress + maxSteps; 350 editAddress = editAddress % maxSteps; 351 } 352 353 if (nextAddressRead.process(params[MANUAL_CLOCK_PARAM].getValue()) || clockTrigger.process(inputs[TRG_INPUT].getVoltage() / 2.f)) { 354 numAddresses = (int) clamp(roundf(params[STEPS_PARAM].getValue() /*+ inputs[STEPS_INPUT].getVoltage()*/), 1.0f, 16.0f); 355 356 address = address + 1; 357 address = address % numAddresses; 358 } 359 360 if (resetTriggerButton.process(params[RESET_PARAM].getValue()) || resetTriggerInput.process(inputs[RESET_INPUT].getVoltage() / 2.f)) { 361 numAddresses = (int) clamp(roundf(params[STEPS_PARAM].getValue()), 1.0f, 16.0f); 362 363 address = 0; 364 } 365 366 addressPlusOne = address + 1; 367 editAddressPlusOne = editAddress + 1; 368 369 for (int i = 0 ; i < 10 ; i++) 370 { 371 for (int c = 0; c < 16; c++) { 372 input_values[i * 16 + c] = inputs[INPUT_JACKS + i].getVoltage(c); 373 } 374 } 375 376 for (int i = 0 ; i < 10 ; i++) 377 { 378 for (int j = 0 ; j < 10 ; j++) 379 { 380 // todo: toggle for each output of how to combine multiple active signals in a column 381 // sum, average, and, or etc 382 if (switch_states[address][i][j]) { 383 for (int c = 0; c < channelCount[j]; c++) { 384 sums[j * 16 + c] += input_values[i * 16 + c]; 385 } 386 387 } 388 } 389 } 390 /// outputs 391 for (int j = 0 ; j < 10 ; j++) 392 { 393 //outputs[OUTPUTS + j].setChannels(16); 394 for (int c = 0; c < channelCount[j]; c++) { 395 outputs[OUTPUTS + j].setVoltage(sums[j * 16 + c], c); 396 } 397 } 398 } 399 400 //////////////////////////////////// 401 struct NumberDisplayWidget3 : TransparentWidget { 402 403 int *value; 404 ComputerscarePatchSequencer *module; 405 std::string fontPath = "res/Segment7Standard.ttf"; 406 407 NumberDisplayWidget3() { 408 409 }; 410 411 void draw(const DrawArgs &args) override 412 { 413 // Background 414 NVGcolor backgroundColor = nvgRGB(0x00, 0x00, 0x00); 415 416 nvgBeginPath(args.vg); 417 nvgRoundedRect(args.vg, 0.0, 0.0, box.size.x, box.size.y, 4.0); 418 nvgFillColor(args.vg, backgroundColor); 419 nvgFill(args.vg); 420 421 } 422 void drawLayer(const BGPanel::DrawArgs& args, int layer) override { 423 if (layer == 1) { 424 drawText(args); 425 } 426 Widget::drawLayer(args, layer); 427 } 428 void drawText(const BGPanel::DrawArgs& args) { 429 std::shared_ptr<Font> font = APP->window->loadFont(asset::plugin(pluginInstance, fontPath)); 430 if (font) { 431 // text 432 nvgFontSize(args.vg, 18); 433 nvgFontFaceId(args.vg, font->handle); 434 nvgTextLetterSpacing(args.vg, 2.5); 435 436 std::stringstream to_display; 437 if (module) { 438 to_display << std::setw(3) << *value; 439 } 440 else { 441 to_display << std::setw(3) << "16"; 442 } 443 444 Vec textPos = Vec(6.0f, 17.0f); 445 NVGcolor textColor = nvgRGB(0xC0, 0xE7, 0xDE); 446 nvgFillColor(args.vg, textColor); 447 nvgText(args.vg, textPos.x, textPos.y, to_display.str().c_str(), NULL); 448 } 449 } 450 }; 451 452 453 454 struct ComputerscarePatchSequencerWidget : ModuleWidget { 455 456 ComputerscarePatchSequencerWidget(ComputerscarePatchSequencer *module) { 457 setModule(module); 458 setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscarePatchSequencerPanel.svg"))); 459 460 int top_row = 70; 461 int row_spacing = 26; 462 int column_spacing = 26; 463 464 int rdx = rand() % 8; 465 int rdy = rand() % 8; 466 467 for (int i = 0 ; i < 10 ; i++) 468 { 469 470 471 for (int j = 0 ; j < 10 ; j++ ) 472 { 473 // the part you click 474 addParam(createParam<LEDButton>(Vec(35 + column_spacing * j + 2, top_row + row_spacing * i + 4), module, ComputerscarePatchSequencer::SWITCHES + i + j * 10)); 475 476 477 478 // green light indicates the state of the matrix that is being edited 479 //ModuleLightWidget *bigOne = ModuleLightWidget::create<ComputerscareHugeLight<ComputerscareGreenLight>>(Vec(35 + column_spacing * j +0.4, top_row + row_spacing * i +2.4 ), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10); 480 addChild(createLight<ComputerscareHugeLight<ComputerscareGreenLight>>(Vec(35 + column_spacing * j + 0.4, top_row + row_spacing * i + 2.4 ), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10)); 481 482 483 484 //addParam(createParam<LEDButton>(Vec(35 + column_spacing * j+2, top_row + row_spacing * i+4), module, ComputerscarePatchSequencer::SWITCHES + i + j * 10)); 485 486 487 488 //addChild(bigOne); 489 490 double xpos = 35 + column_spacing * j + 6.3 + rand() % 8 - 4; 491 double ypos = top_row + row_spacing * i + 8.3 + rand() % 8 - 4; 492 // red light indicates the state of the matrix that is the active step 493 //computerscarered 494 //addParam(createParam<ComputerscareSmallLight>(Vec(xpos, ypos), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10 + 100)); 495 addChild(createLight<ComputerscareSmallLight<ComputerscareRedLight>>(Vec(xpos - rdy, ypos + rdx), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10 + 100)); 496 497 addChild(createLight<ComputerscareSmallLight<ComputerscareRedLight>>(Vec(xpos + rdx, ypos + rdy), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10 + 100)); 498 499 } 500 501 addInput(createInput<InPort>(Vec(3, i * row_spacing + top_row), module, ComputerscarePatchSequencer::INPUT_JACKS + i)); 502 503 if (i % 2) { 504 addOutput(createOutput<PointingUpPentagonPort>(Vec(33 + i * column_spacing , top_row + 10 * row_spacing), module, ComputerscarePatchSequencer::OUTPUTS + i)); 505 } 506 else { 507 addOutput(createOutput<InPort>(Vec(33 + i * column_spacing , top_row + 10 * row_spacing), module, ComputerscarePatchSequencer::OUTPUTS + i)); 508 } 509 } 510 511 //clock input 512 addInput(createInput<InPort>(Vec(24, 37), module, ComputerscarePatchSequencer::TRG_INPUT)); 513 514 //reset input 515 addInput(createInput<InPort>(Vec(24, 3), module, ComputerscarePatchSequencer::RESET_INPUT)); 516 517 //manual clock button 518 addParam(createParam<LEDButton>(Vec(7 , 37), module, ComputerscarePatchSequencer::MANUAL_CLOCK_PARAM)); 519 520 //reset button 521 addParam(createParam<LEDButton>(Vec(7 , 3), module, ComputerscarePatchSequencer::RESET_PARAM)); 522 523 //randomize input 524 addInput(createInput<InPort>(Vec(270, 0), module, ComputerscarePatchSequencer::RANDOMIZE_INPUT)); 525 526 //active step display 527 NumberDisplayWidget3 *display = new NumberDisplayWidget3(); 528 display->box.pos = Vec(56, 40); 529 display->box.size = Vec(50, 20); 530 display->value = &module->addressPlusOne; 531 display->module = module; 532 addChild(display); 533 534 // number of steps display 535 NumberDisplayWidget3 *stepsDisplay = new NumberDisplayWidget3(); 536 stepsDisplay->box.pos = Vec(150, 40); 537 stepsDisplay->box.size = Vec(50, 20); 538 stepsDisplay->module = module; 539 stepsDisplay->value = &module->numAddresses; 540 addChild(stepsDisplay); 541 542 //number-of-steps dial. Discrete, 16 positions 543 ParamWidget* stepsKnob = createParam<LrgKnob>(Vec(108, 30), module, ComputerscarePatchSequencer::STEPS_PARAM); 544 addParam(stepsKnob); 545 546 //editAddressNext button 547 addParam(createParam<LEDButton>(Vec(227 , 41), module, ComputerscarePatchSequencer::EDIT_PARAM)); 548 549 //editAddressPrevious button 550 addParam(createParam<LEDButton>(Vec(208 , 41), module, ComputerscarePatchSequencer::EDIT_PREV_PARAM)); 551 552 // currently editing step #: 553 NumberDisplayWidget3 *displayEdit = new NumberDisplayWidget3(); 554 displayEdit->box.pos = Vec(246, 40); 555 displayEdit->box.size = Vec(50, 20); 556 displayEdit->module = module; 557 displayEdit->value = &module->editAddressPlusOne; 558 addChild(displayEdit); 559 fatherSon = module; 560 } 561 562 563 /* void fromJson(json_t *rootJ) override 564 { 565 ModuleWidget::fromJson(rootJ); 566 json_t *button_statesJ = json_object_get(rootJ, "buttons"); 567 if (button_statesJ) { 568 //there be legacy JSON 569 fatherSon->dataFromJson(rootJ); 570 } 571 }*/ 572 void appendContextMenu(Menu *menu) override; 573 574 ComputerscarePatchSequencer *fatherSon; 575 }; 576 struct OnlyRandomizeActiveMenuItem : MenuItem { 577 ComputerscarePatchSequencer *patchSequencer; 578 OnlyRandomizeActiveMenuItem() { 579 580 } 581 void onAction(const event::Action &e) override { 582 patchSequencer->onlyRandomizeActive = !patchSequencer->onlyRandomizeActive; 583 } 584 void step() override { 585 rightText = patchSequencer->onlyRandomizeActive ? "✔" : ""; 586 MenuItem::step(); 587 } 588 }; 589 struct WhichStepToRandomizeItem : MenuItem { 590 ComputerscarePatchSequencer *patchSequencer; 591 int stepEnum; 592 void onAction(const event::Action &e) override { 593 patchSequencer->setRandomizationStepEnum(stepEnum); 594 } 595 void step() override { 596 rightText = CHECKMARK(patchSequencer->getRandomizationStepEnum() == stepEnum); 597 MenuItem::step(); 598 } 599 }; 600 601 struct WhichRandomizationOutputBoundsItem : MenuItem { 602 ComputerscarePatchSequencer *patchSequencer; 603 int boundsEnum; 604 void onAction(const event::Action &e) override { 605 patchSequencer->setRandomizationOutputBoundsEnum(boundsEnum); 606 } 607 void step() override { 608 rightText = CHECKMARK(patchSequencer->getRandomizationOutputBoundsEnum() == boundsEnum); 609 MenuItem::step(); 610 } 611 }; 612 613 struct FatherSonChannelItem : MenuItem { 614 ComputerscarePatchSequencer *module; 615 int channels; 616 void onAction(const event::Action &e) override { 617 module->channelCountEnum = channels; 618 } 619 }; 620 621 622 struct FatherSonChannelsItem : MenuItem { 623 ComputerscarePatchSequencer *module; 624 Menu *createChildMenu() override { 625 Menu *menu = new Menu; 626 for (int channels = -1; channels <= 16; channels++) { 627 FatherSonChannelItem *item = new FatherSonChannelItem; 628 if (channels < 0) { 629 item->text = "Automatic"; 630 } 631 else { 632 item->text = string::f("%d", channels); 633 } 634 item->rightText = CHECKMARK(module->channelCountEnum == channels); 635 item->module = module; 636 item->channels = channels; 637 menu->addChild(item); 638 } 639 return menu; 640 } 641 }; 642 643 void ComputerscarePatchSequencerWidget::appendContextMenu(Menu *menu) 644 { 645 ComputerscarePatchSequencer *patchSequencer = dynamic_cast<ComputerscarePatchSequencer *>(this->module); 646 647 MenuLabel *spacerLabel = new MenuLabel(); 648 menu->addChild(spacerLabel); 649 650 FatherSonChannelsItem *channelsItem = new FatherSonChannelsItem; 651 channelsItem->text = "Output Polyphony"; 652 channelsItem->rightText = RIGHT_ARROW; 653 channelsItem->module = patchSequencer; 654 menu->addChild(channelsItem); 655 656 menu->addChild(new MenuEntry); 657 658 659 MenuLabel *modeLabel = new MenuLabel(); 660 modeLabel->text = "Randomization Options"; 661 menu->addChild(modeLabel); 662 663 664 OnlyRandomizeActiveMenuItem *onlyRandomizeActiveMenuItem = new OnlyRandomizeActiveMenuItem(); 665 onlyRandomizeActiveMenuItem->text = "Only Randomize Active Connections"; 666 onlyRandomizeActiveMenuItem->patchSequencer = patchSequencer; 667 menu->addChild(onlyRandomizeActiveMenuItem); 668 669 menu->addChild(construct<MenuLabel>()); 670 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Which Step to Randomize")); 671 menu->addChild(construct<WhichStepToRandomizeItem>(&MenuItem::text, "Edit step", &WhichStepToRandomizeItem::patchSequencer, patchSequencer, &WhichStepToRandomizeItem::stepEnum, 0)); 672 menu->addChild(construct<WhichStepToRandomizeItem>(&MenuItem::text, "Active step", &WhichStepToRandomizeItem::patchSequencer, patchSequencer, &WhichStepToRandomizeItem::stepEnum, 1)); 673 menu->addChild(construct<WhichStepToRandomizeItem>(&MenuItem::text, "All steps", &WhichStepToRandomizeItem::patchSequencer, patchSequencer, &WhichStepToRandomizeItem::stepEnum, 2)); 674 675 676 // randomization output bounds 677 menu->addChild(construct<MenuLabel>()); 678 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Output Row Randomization Method")); 679 menu->addChild(construct<WhichRandomizationOutputBoundsItem>(&MenuItem::text, "One or none", &WhichRandomizationOutputBoundsItem::patchSequencer, patchSequencer, &WhichRandomizationOutputBoundsItem::boundsEnum, 0)); 680 menu->addChild(construct<WhichRandomizationOutputBoundsItem>(&MenuItem::text, "Exactly one", &WhichRandomizationOutputBoundsItem::patchSequencer, patchSequencer, &WhichRandomizationOutputBoundsItem::boundsEnum, 1)); 681 menu->addChild(construct<WhichRandomizationOutputBoundsItem>(&MenuItem::text, "Zero or more", &WhichRandomizationOutputBoundsItem::patchSequencer, patchSequencer, &WhichRandomizationOutputBoundsItem::boundsEnum, 2)); 682 menu->addChild(construct<WhichRandomizationOutputBoundsItem>(&MenuItem::text, "One or more", &WhichRandomizationOutputBoundsItem::patchSequencer, patchSequencer, &WhichRandomizationOutputBoundsItem::boundsEnum, 3)); 683 684 } 685 686 Model *modelComputerscarePatchSequencer = createModel<ComputerscarePatchSequencer, ComputerscarePatchSequencerWidget>("computerscare-fatherandson"); 687