ComputerscareHorseADoodleDoo.cpp (34934B)
1 #include "Computerscare.hpp" 2 3 #include "dtpulse.hpp" 4 5 #include <iostream> 6 #include <string> 7 #include <sstream> 8 9 struct ComputerscareHorseADoodleDoo; 10 11 const std::string HorseAvailableModes[4] = {"Each channel outputs independent pulse & CV sequence", "All channels triggered by Ch. 1 sequence", "Trigger Cascade:\nEach channel is triggered by the previous channel's trigger sequence", "EOC Cascade:\nEach channel is triggered by the previous channel's EOC"}; 12 const std::string HorseAvailableGateModes[2] = {"Pass through the clock signal for each gate", "Variable gates"}; 13 14 15 struct HorseModeParam : ParamQuantity { 16 std::string getDisplayValueString() override { 17 int val = getValue(); 18 return HorseAvailableModes[val]; 19 } 20 }; 21 22 struct HorseGateModeParam : ParamQuantity { 23 std::string getDisplayValueString() override { 24 int val = getValue(); 25 return HorseAvailableGateModes[val]; 26 } 27 }; 28 29 30 struct HorseSequencer { 31 float pattern = 0.f; 32 int numSteps = 8; 33 int currentStep = -1; 34 float density = 0.5f; 35 float phase = 0.f; 36 float phase2 = 0.f; 37 float gatePhase = 0.f; 38 39 float pendingPattern = 0.f; 40 int pendingNumSteps = 8; 41 float pendingDensity = 0.5f; 42 float pendingPhase = 0.f; 43 float pendingPhase2 = 0.f; 44 float pendingGatePhase = 0.f; 45 bool pendingChange = 0; 46 bool forceChange = 0; 47 48 49 50 int primes[16] = {30011, 36877, 26627, 32833, 66797, 95153, 66553, 84857, 32377, 79589, 25609, 20113, 70991, 86533, 21499, 32491}; 51 int otherPrimes[16] = {80651, 85237, 11813, 22343, 19543, 28027, 9203, 39521, 42853, 58411, 33811, 76771, 10939, 22721, 17851, 10163}; 52 int channel = 0; 53 54 std::vector<int> absoluteSequence; 55 std::vector<float> cvSequence; 56 std::vector<float> cv2Sequence; 57 std::vector<float> gateLengthSequence; 58 59 std::vector<int> timeToNextStep; 60 61 62 HorseSequencer() { 63 64 } 65 HorseSequencer(float patt, int steps, float dens, int ch, float phi, float phi2, float gatePhi) { 66 numSteps = steps; 67 density = dens; 68 pattern = patt; 69 channel = ch; 70 phase = phi; 71 phase2 = phi2; 72 gatePhase = gatePhi; 73 makeAbsolute(); 74 } 75 void makeAbsolute() { 76 std::vector<int> newSeq; 77 std::vector<float> newCV; 78 std::vector<float> newCV2; 79 std::vector<float> newGateLength; 80 81 newSeq.resize(0); 82 newCV.resize(0); 83 newCV2.resize(0); 84 newGateLength.resize(0); 85 86 87 88 float cvRoot = 0.f; 89 float cv2Root = 0.f; 90 float trigConst = 2 * M_PI / ((float)numSteps); 91 92 for (int i = 0; i < numSteps; i++) { 93 float val = 0.f; 94 float cvVal = 0.f; 95 float cv2Val = 0.f; 96 float gateLengthVal = 0.f; 97 int glv = 0; 98 float arg = pattern + ((float) i) * trigConst; 99 for (int k = 0; k < 4; k++) { 100 int trgArgIndex = ((i + 1) * (k + 1)) % 16; 101 int trgThetaIndex = (otherPrimes[0] + i) % 16; 102 103 int cvArgIndex = ((i + 11) * (k + 1) + 201) % 16; 104 int cvThetaIndex = (otherPrimes[3] + i - 7) % 16; 105 106 int cv2ArgIndex = ((i + 12) * (k + 2) + 31) % 16; 107 int cv2ThetaIndex = (otherPrimes[6] + i - 17) % 16; 108 109 int gateLengthArgIndex = ((i + 13) * (k + 3) + 101) % 16; 110 int gateThetaIndex = (otherPrimes[4] + 3 * i - 17) % 16; 111 112 val += std::sin(primes[trgArgIndex] * arg + otherPrimes[trgThetaIndex]); 113 cvVal += std::sin(primes[cvArgIndex] * arg + otherPrimes[cvThetaIndex] + phase); 114 115 cv2Val += std::sin(primes[cv2ArgIndex] * arg + otherPrimes[cv2ThetaIndex] + phase2); 116 gateLengthVal += std::sin(primes[gateLengthArgIndex] * arg + otherPrimes[gateThetaIndex] + gatePhase); 117 118 } 119 newSeq.push_back(val < (density - 0.5) * 4 * 2 ? 1 : 0); 120 newCV.push_back(cvRoot + (cvVal + 4) / .8); 121 newCV2.push_back(cv2Root + (cv2Val + 4) / .8); 122 123 //quantized to 16 lengths 124 newGateLength.push_back(std::floor(8 + 2 * gateLengthVal)); 125 } 126 //printVector(newSeq); 127 absoluteSequence = newSeq; 128 cvSequence = newCV; 129 cv2Sequence = newCV2; 130 gateLengthSequence = newGateLength; 131 132 setTimeToNextStep(); 133 } 134 void checkAndArm(float patt, int steps, float dens, float phi, float phi2, float gatePhi) { 135 136 if (pattern != patt || numSteps != steps || density != dens || phase != phi || phase2 != phi2 || gatePhase != gatePhi) { 137 pendingPattern = patt; 138 pendingNumSteps = steps; 139 pendingDensity = dens; 140 pendingPhase = phi; 141 pendingPhase2 = phi2; 142 pendingGatePhase = gatePhi; 143 pendingChange = true; 144 } 145 } 146 void armChange() { 147 forceChange = true; 148 } 149 void disarm() { 150 pendingChange = false; 151 pendingPattern = pattern; 152 pendingNumSteps = numSteps; 153 pendingDensity = density; 154 pendingPhase = phase; 155 pendingPhase2 = phase2; 156 pendingGatePhase = gatePhase; 157 } 158 void change(float patt, int steps, float dens, float phi, float phi2, float gatePhi) { 159 numSteps = std::max(1, steps); 160 density = std::fmax(0, dens); 161 pattern = patt; 162 phase = phi; 163 phase2 = phi2; 164 gatePhase = gatePhi; 165 currentStep = 0; 166 makeAbsolute(); 167 168 } 169 void tick() { 170 currentStep++; 171 currentStep %= numSteps; 172 if ((currentStep == 0 && pendingChange) || forceChange) { 173 change(pendingPattern, pendingNumSteps, pendingDensity, pendingPhase, pendingPhase2, pendingGatePhase); 174 pendingChange = false; 175 forceChange = false; 176 currentStep = 0; 177 } 178 } 179 void setTimeToNextStep() { 180 timeToNextStep.resize(0); 181 timeToNextStep.resize(numSteps); 182 int counter = 0; 183 int timeIndex = 0; 184 for (unsigned int i = 0; i < numSteps * 2; i++) { 185 if (absoluteSequence[i % numSteps]) { 186 timeToNextStep[timeIndex % numSteps] = counter; 187 timeIndex = i; 188 counter = 1; 189 } 190 else { 191 timeToNextStep[i % numSteps] = 0; 192 counter++; 193 } 194 } 195 } 196 void reset() { 197 currentStep = 0; 198 } 199 int get() { 200 return absoluteSequence[currentStep]; 201 } 202 float getCV() { 203 return cvSequence[currentStep]; 204 } 205 float getCV2() { 206 return cv2Sequence[currentStep]; 207 } 208 float getGateLength() { 209 return gateLengthSequence[currentStep]; 210 } 211 int getTimeToNextStep() { 212 return timeToNextStep[currentStep]; 213 } 214 int tickAndGet() { 215 tick(); 216 return get(); 217 } 218 int getNumSteps() { 219 return numSteps; 220 } 221 }; 222 223 struct ComputerscareHorseADoodleDoo : ComputerscareMenuParamModule { 224 int counter = 0; 225 float currentValues[16] = {0.f}; 226 bool atFirstStepPoly[16] = {false}; 227 int previousStep[16] = { -1}; 228 bool shouldSetEOCHigh[16] = {false}; 229 bool shouldOutputPulse[16] = {false}; 230 231 enum ParamIds { 232 PATTERN_KNOB, 233 PATTERN_TRIM, 234 STEPS_KNOB, 235 STEPS_TRIM, 236 DENSITY_KNOB, 237 DENSITY_TRIM, 238 WEIRDNESS_KNOB, 239 WEIRDNESS_TRIM, 240 POLY_KNOB, 241 MODE_KNOB, 242 MANUAL_RESET_BUTTON, 243 PATTERN_SPREAD, 244 STEPS_SPREAD, 245 DENSITY_SPREAD, 246 MANUAL_CLOCK_BUTTON, 247 CV_SCALE, 248 CV_OFFSET, 249 CV_PHASE, 250 GATE_MODE, 251 GATE_LENGTH_SCALE, 252 GATE_LENGTH_OFFSET, 253 GATE_LENGTH_PHASE, 254 CV2_SCALE, 255 CV2_OFFSET, 256 CV2_PHASE, 257 NUM_PARAMS 258 }; 259 enum InputIds { 260 CLOCK_INPUT, 261 RESET_INPUT, 262 PATTERN_CV, 263 STEPS_CV, 264 DENSITY_CV, 265 NUM_INPUTS 266 }; 267 enum OutputIds { 268 TRIGGER_OUTPUT, 269 EOC_OUTPUT, 270 CV_OUTPUT, 271 CV2_OUTPUT, 272 NUM_OUTPUTS 273 }; 274 enum LightIds { 275 NUM_LIGHTS 276 }; 277 278 rack::dsp::SchmittTrigger clockInputTrigger[16]; 279 rack::dsp::SchmittTrigger resetInputTrigger[16]; 280 281 rack::dsp::SchmittTrigger clockManualTrigger; 282 rack::dsp::SchmittTrigger globalManualResetTrigger; 283 284 dsp::Timer syncTimer[16]; 285 286 dsp::PulseGenerator gatePulse[16]; 287 288 float lastPatternKnob = 0.f; 289 int lastStepsKnob = 2; 290 float lastDensityKnob = 0.f; 291 int lastPolyKnob = 0; 292 float lastPhaseKnob = 0.f; 293 float lastGatePhaseKnob = 0.f; 294 295 float cvOffset = 0.f; 296 float cvScale = 1.f; 297 298 float lastPhase2Knob = 0.f; 299 float cv2Offset = 0.f; 300 float cv2Scale = 1.f; 301 302 float gateLengthOffset = 0.f; 303 float gateLengthScale = 1.f; 304 305 int mode = 1; 306 307 int seqVal[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 308 float cvVal[16] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; 309 float cv2Val[16] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; 310 311 int clockChannels[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 312 int resetChannels[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 313 314 bool changePending[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 315 316 float gateTimeFactor[16] = {.999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f, .999f}; 317 318 float syncTime[16] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; 319 HorseSequencer seq[16]; 320 321 struct HorsePatternParamQ: ParamQuantity { 322 virtual std::string getPatternString() { 323 return dynamic_cast<ComputerscareHorseADoodleDoo*>(module)->getPatternDisplay(); 324 } 325 std::string getDisplayValueString() override { 326 float val = getValue(); 327 return std::to_string(val) + "\n" + getPatternString(); 328 } 329 }; 330 struct HorsePatternSpreadParam: ParamQuantity { 331 virtual std::string getAllPolyChannelsPatternDisplayString() { 332 return dynamic_cast<ComputerscareHorseADoodleDoo*>(module)->getPatternDisplay(true, false); 333 } 334 std::string getDisplayValueString() override { 335 float val = getValue(); 336 return std::to_string(100 * val) + "%\n" + getAllPolyChannelsPatternDisplayString(); 337 } 338 }; 339 340 struct HorseStepsSpreadParam: ParamQuantity { 341 std::string newLineSepIntVector(std::vector<int> vec) { 342 std::string out = ""; 343 for (unsigned int i = 0; i < vec.size(); i++) { 344 out = out + std::to_string(vec[i]) + "\n"; 345 } 346 return out; 347 } 348 virtual std::string allStepsDisplay() { 349 return dynamic_cast<ComputerscareHorseADoodleDoo*>(module)->getAllStepsDisplay(); 350 } 351 std::string getDisplayValueString() override { 352 float val = getValue(); 353 return std::to_string(100 * val) + "%\n" + allStepsDisplay(); 354 } 355 }; 356 357 struct HorseDensitySpreadParam: ParamQuantity { 358 virtual std::string getAllPolyChannelsDisplayString() { 359 return dynamic_cast<ComputerscareHorseADoodleDoo*>(module)->getAllDensityDisplay(); 360 } 361 std::string getDisplayValueString() override { 362 float val = getValue(); 363 return std::to_string(100 * val) + "%\n" + getAllPolyChannelsDisplayString(); 364 } 365 }; 366 367 struct HorseResetParamQ: ParamQuantity { 368 virtual std::string getResetTransportDisplay() { 369 return dynamic_cast<ComputerscareHorseADoodleDoo*>(module)->getResetTransportDisplay(); 370 } 371 std::string getDisplayValueString() override { 372 return "\n" + getResetTransportDisplay(); 373 } 374 }; 375 376 ComputerscareHorseADoodleDoo() { 377 378 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 379 380 configParam<HorsePatternParamQ>(PATTERN_KNOB, 0.f, 10.f, 0.f, "Pattern"); 381 configParam(STEPS_KNOB, 2.f, 64.f, 8.f, "Number of Steps"); 382 configParam(DENSITY_KNOB, 0.f, 1.f, 0.5f, "Density", "%", 0, 100); 383 384 configParam(PATTERN_TRIM, -1.f, 1.f, 0.f, "Pattern CV Trim"); 385 configParam(STEPS_TRIM, -1.f, 1.f, 0.f, "Steps CV Trim"); 386 configParam(DENSITY_TRIM, -1.f, 1.f, 0.f, "Density CV Trim"); 387 388 389 configParam<HorsePatternSpreadParam>(PATTERN_SPREAD, 0.f, 1.f, 0.5f, "Pattern Spread", "", 0, 100); 390 configParam<HorseStepsSpreadParam>(STEPS_SPREAD, -1.f, 1.f, 0.f, "Steps Spread", "", 0, 100); 391 configParam<HorseDensitySpreadParam>(DENSITY_SPREAD, -1.f, 1.f, 0.f, "Density Spread", "", 0, 100); 392 393 configParam<AutoParamQuantity>(POLY_KNOB, 0.f, 16.f, 0.f, "Polyphony"); 394 395 configParam<HorseModeParam>(MODE_KNOB, 0.f, 3.f, 0.f, "Mode"); 396 397 configParam<HorseModeParam>(GATE_MODE, 0.f, 1.f, 1.f, "Gate Mode"); 398 399 configParam<HorseResetParamQ>(MANUAL_RESET_BUTTON, 0.f, 1.f, 0.f, "Reset all Sequences"); 400 configParam(MANUAL_CLOCK_BUTTON, 0.f, 1.f, 0.f, "Advance all Sequences"); 401 402 configMenuParam(CV_SCALE, -2.f, 2.f, 1.f, "CV Scale", 2); 403 configMenuParam(CV_OFFSET, -10.f, 10.f, 0.f, "CV Offset", 2); 404 configMenuParam(CV_PHASE, -3.14159f, 3.14159f, 0.f, "CV Variation", 2); 405 406 configMenuParam(CV2_SCALE, -2.f, 2.f, 1.f, "CV2 Scale", 2); 407 configMenuParam(CV2_OFFSET, -10.f, 10.f, 0.f, "CV2 Offset", 2); 408 configMenuParam(CV2_PHASE, -3.14159f, 3.14159f, 0.f, "CV2 Variation", 2); 409 410 configMenuParam(GATE_LENGTH_SCALE, 0.f, 2.f, 1.f, "Gate Length Scaling", 2); 411 configMenuParam(GATE_LENGTH_OFFSET, 0.f, 1.f, 0.f, "Gate Length Minimum", 2); 412 configMenuParam(GATE_LENGTH_PHASE, -3.14159f, 3.14159f, 0.f, "Gate Length Variation", 2); 413 414 getParamQuantity(POLY_KNOB)->randomizeEnabled = false; 415 getParamQuantity(POLY_KNOB)->resetEnabled = false; 416 417 getParamQuantity(MODE_KNOB)->randomizeEnabled = false; 418 getParamQuantity(GATE_MODE)->randomizeEnabled = false; 419 420 getParamQuantity(CV_SCALE)->randomizeEnabled = false; 421 getParamQuantity(CV_OFFSET)->randomizeEnabled = false; 422 getParamQuantity(CV_PHASE)->randomizeEnabled = false; 423 424 getParamQuantity(CV2_SCALE)->randomizeEnabled = false; 425 getParamQuantity(CV2_OFFSET)->randomizeEnabled = false; 426 getParamQuantity(CV2_PHASE)->randomizeEnabled = false; 427 428 getParamQuantity(GATE_LENGTH_SCALE)->randomizeEnabled = false; 429 getParamQuantity(GATE_LENGTH_OFFSET)->randomizeEnabled = false; 430 getParamQuantity(GATE_LENGTH_PHASE)->randomizeEnabled = false; 431 432 getParamQuantity(PATTERN_SPREAD)->randomizeEnabled = false; 433 getParamQuantity(STEPS_SPREAD)->randomizeEnabled = false; 434 getParamQuantity(DENSITY_SPREAD)->randomizeEnabled = false; 435 436 configInput(CLOCK_INPUT, "Clock"); 437 configInput(RESET_INPUT, "Reset"); 438 configInput(PATTERN_CV, "Pattern CV"); 439 configInput(STEPS_CV, "Number of Steps CV"); 440 configInput(DENSITY_CV, "Density CV"); 441 442 configOutput(TRIGGER_OUTPUT, "Trigger Sequence"); 443 configOutput(EOC_OUTPUT, "End of Cycle"); 444 configOutput(CV_OUTPUT, "CV Sequence"); 445 configOutput(CV2_OUTPUT, "2nd CV Sequence"); 446 447 for (int i = 0; i < 16; i++) { 448 seq[i] = HorseSequencer(0.f, 8, 0.f, i, 0.f, 0.f, 0.f); 449 previousStep[i] = -1; 450 } 451 452 453 } 454 455 std::vector<int> getAllSteps() { 456 std::vector<int> out = {}; 457 for (int i = 0; i < polyChannels; i++) { 458 out.push_back(seq[i].getNumSteps()); 459 } 460 return out; 461 } 462 std::string getAllPatternValueDisplay(std::string sep = "\n") { 463 std::string out = ""; 464 465 int channelToDisplay; 466 for (int i = 0; i < polyChannels; i++) { 467 468 channelToDisplay = (mode == 1) ? 0 : i; 469 out += "ch " + string::f("%*d", 2, i + 1) + ": "; 470 if (seq[i].pendingChange) { 471 out = out + std::to_string(seq[channelToDisplay].pendingPattern); 472 out = out + " (" + std::to_string(seq[channelToDisplay].pattern) + ")"; 473 } 474 else { 475 out = out + std::to_string(seq[channelToDisplay].pattern); 476 } 477 out += sep; 478 } 479 return out; 480 } 481 482 std::string getAllStepsDisplay(std::string sep = "\n") { 483 std::string out = ""; 484 for (int i = 0; i < polyChannels; i++) { 485 out += "ch " + string::f("%*d", 2, i + 1) + ": "; 486 if (seq[i].pendingChange) { 487 out = out + std::to_string(seq[i].pendingNumSteps); 488 out = out + " (" + std::to_string(seq[i].numSteps) + ")"; 489 } 490 else { 491 out = out + std::to_string(seq[i].getNumSteps()); 492 } 493 out += sep; 494 } 495 return out; 496 } 497 std::string getAllDensityDisplay(std::string sep = "\n") { 498 std::string out = ""; 499 for (int i = 0; i < polyChannels; i++) { 500 501 out += "ch " + string::f("%*d", 2, i + 1) + ": "; 502 if (seq[i].pendingChange) { 503 out = out + string::f("%.*g", 3, 100 * seq[i].pendingDensity) + "%"; 504 out = out + " (" + string::f("%.*g", 3, 100 * seq[i].density) + "%)"; 505 } 506 else { 507 out = out + string::f("%.*g", 3, 100 * seq[i].density) + "%"; 508 } 509 out += sep; 510 } 511 return out; 512 } 513 std::string getResetTransportDisplay(std::string sep = "\n") { 514 std::string out = ""; 515 for (int i = 0; i < polyChannels; i++) { 516 517 out += "ch " + string::f("%*d", 2, i + 1) + ": "; 518 out = out + string::f("%*d", 4, seq[i].currentStep + 1); 519 out = out + " / " + string::f("%*d", 4, seq[i].numSteps); 520 521 out += sep; 522 } 523 return out; 524 } 525 std::string getPatternDisplay(bool showPatternValue = false, bool showTransport = true, std::string sep = "\n") { 526 std::string out = ""; 527 int channelToDisplay; 528 for (int i = 0; i < polyChannels; i++) { 529 channelToDisplay = (mode == 1) ? 0 : i; 530 out += "ch " + string::f("%*d", 2, i + 1) + ": "; 531 int current = seq[channelToDisplay].currentStep; 532 533 if (showPatternValue) { 534 out += std::to_string(seq[channelToDisplay].pattern) + " "; 535 } 536 for (int j = 0; j < seq[channelToDisplay].numSteps; j++) { 537 538 bool highStep = seq[channelToDisplay].absoluteSequence[j] == 1; 539 540 out += (showTransport && current == j) ? (highStep ? "☺" : "☹") : ( highStep ? "x" : "_"); 541 out += j % 192 == 191 ? "\n" : ""; 542 } 543 544 out += sep; 545 } 546 return out; 547 } 548 549 void setMode(int newMode) { 550 params[MODE_KNOB].setValue(newMode); 551 } 552 void setGateMode(int newGateMode) { 553 params[GATE_MODE].setValue(newGateMode); 554 } 555 556 void checkKnobChanges() { 557 558 int pattNum = inputs[PATTERN_CV].getChannels(); 559 int stepsNum = inputs[STEPS_CV].getChannels(); 560 int densityNum = inputs[DENSITY_CV].getChannels(); 561 562 int clockNum = inputs[CLOCK_INPUT].getChannels(); 563 int resetNum = inputs[RESET_INPUT].getChannels(); 564 565 cvScale = params[CV_SCALE].getValue(); 566 cvOffset = params[CV_OFFSET].getValue(); 567 568 cv2Scale = params[CV2_SCALE].getValue(); 569 cv2Offset = params[CV2_OFFSET].getValue(); 570 571 gateLengthOffset = params[GATE_LENGTH_OFFSET].getValue(); 572 gateLengthScale = params[GATE_LENGTH_SCALE].getValue(); 573 574 mode = params[MODE_KNOB].getValue(); 575 lastStepsKnob = std::floor(params[STEPS_KNOB].getValue()); 576 lastPolyKnob = std::floor(params[POLY_KNOB].getValue()); 577 578 lastPhaseKnob = params[CV_PHASE].getValue(); 579 lastPhase2Knob = params[CV2_PHASE].getValue(); 580 lastGatePhaseKnob = params[GATE_LENGTH_PHASE].getValue(); 581 582 polyChannels = lastPolyKnob == 0 ? std::max(clockNum, std::max(pattNum, std::max(stepsNum, densityNum))) : lastPolyKnob; 583 584 for (int i = 0; i < 16; i++) { 585 clockChannels[i] = std::max(1, std::min(i + 1, clockNum)); 586 resetChannels[i] = std::max(1, std::min(i + 1, resetNum)); 587 } 588 589 outputs[TRIGGER_OUTPUT].setChannels(polyChannels); 590 outputs[EOC_OUTPUT].setChannels(polyChannels); 591 outputs[CV_OUTPUT].setChannels(polyChannels); 592 outputs[CV2_OUTPUT].setChannels(polyChannels); 593 594 for (int i = 0; i < polyChannels; i++) { 595 float patternVal = params[PATTERN_KNOB].getValue() + params[PATTERN_TRIM].getValue() * inputs[PATTERN_CV].getVoltage(pattNum == 1 ? 0 : fmin(i, pattNum)); 596 int stepsVal = std::floor(params[STEPS_KNOB].getValue() + params[STEPS_TRIM].getValue() * inputs[STEPS_CV].getVoltage(stepsNum == 1 ? 0 : fmin(i, stepsNum))); 597 float densityVal = params[DENSITY_KNOB].getValue() + params[DENSITY_TRIM].getValue() * inputs[DENSITY_CV].getVoltage(densityNum == 1 ? 0 : fmin(i, densityNum)) / 10; 598 599 patternVal += i * params[PATTERN_SPREAD].getValue(); 600 stepsVal += std::floor(params[STEPS_SPREAD].getValue() * i * stepsVal); 601 densityVal += params[DENSITY_SPREAD].getValue() * i / 10; 602 603 stepsVal = std::max(2, stepsVal); 604 densityVal = std::fmax(0, std::fmin(1, densityVal)); 605 606 seq[i].checkAndArm(patternVal, stepsVal, densityVal, lastPhaseKnob, lastPhase2Knob, lastGatePhaseKnob); 607 } 608 } 609 void processChannel(int ch, bool clocked, bool reset, bool clockInputHigh, int overrideMode = 0, bool overriddenTriggerHigh = false) { 610 bool eocHigh = false; 611 612 int gateMode = params[GATE_MODE].getValue(); 613 614 if (reset) { 615 seq[ch].armChange(); 616 } 617 618 if (clocked /*&& !reset*/) { 619 if (overrideMode == 1) { 620 seqVal[ch] = seq[ch].tickAndGet(); 621 if (overriddenTriggerHigh) { 622 cvVal[ch] = seq[ch].getCV(); 623 cv2Val[ch] = seq[ch].getCV2(); 624 } 625 seqVal[ch] = overriddenTriggerHigh; 626 } 627 else if (overrideMode == 2 || overrideMode == 3) { 628 if (overriddenTriggerHigh) { 629 seqVal[ch] = seq[ch].tickAndGet(); 630 cvVal[ch] = seq[ch].getCV(); 631 cv2Val[ch] = seq[ch].getCV2(); 632 } 633 } 634 else { 635 // no override, operate as normal. Tick sequencer every clock, and tick CV if the trigger is high 636 seqVal[ch] = seq[ch].tickAndGet(); 637 if (seqVal[ch]) { 638 cvVal[ch] = seq[ch].getCV(); 639 cv2Val[ch] = seq[ch].getCV2(); 640 } 641 } 642 643 644 645 646 647 atFirstStepPoly[ch] = (seq[ch].currentStep == 0); 648 649 shouldSetEOCHigh[ch] = atFirstStepPoly[ch] && previousStep[ch] != 0; 650 shouldOutputPulse[ch] = seqVal[ch] == 1 && (previousStep[ch] != seq[ch].currentStep); 651 652 previousStep[ch] = seq[ch].currentStep; 653 654 if (gateMode == 1 && shouldOutputPulse[ch]) { 655 int len = seq[ch].getGateLength(); 656 int ttns = seq[ch].getTimeToNextStep(); 657 float timeLeft = syncTime[0] * ttns; 658 float gateLengthFactor = math::clamp(gateLengthOffset + gateLengthScale * ((float)len) / 16, 0.05f, 0.95f); 659 float ms = timeLeft * gateLengthFactor; 660 if (ch == 0 || ch == 1) { 661 //DEBUG("ch:%i,step:%i,len:%i,ttns:%i,ms:%f", ch, seq[ch].currentStep, len, ttns, ms); 662 } 663 664 gatePulse[ch].reset(); 665 gatePulse[ch].trigger(ms); 666 } 667 668 669 } 670 671 if (true || inputs[CLOCK_INPUT].isConnected()) { 672 673 if (gateMode == 0) { 674 //clock pass-through mode 675 outputs[TRIGGER_OUTPUT].setVoltage((clockInputHigh && shouldOutputPulse[ch]) ? 10.0f : 0.0f, ch); 676 677 } 678 else if (gateMode == 1) { 679 680 //gate mode 681 bool gateHigh = gatePulse[ch].process(APP->engine->getSampleTime()); 682 outputs[TRIGGER_OUTPUT].setVoltage(gateHigh ? 10.0f : 0.0f, ch); 683 684 } 685 //DEBUG("before output:%f",cvVal); 686 outputs[CV_OUTPUT].setVoltage(cvScale * cvVal[ch] + cvOffset, ch); 687 outputs[CV2_OUTPUT].setVoltage(cv2Scale * cv2Val[ch] + cv2Offset, ch); 688 //outputs[EOC_OUTPUT].setVoltage((currentTriggerIsHigh && atFirstStepPoly[ch]) ? 10.f : 0.0f, ch); 689 } 690 else { 691 692 } 693 694 //if (atFirstStepPoly[ch]) { 695 outputs[EOC_OUTPUT].setVoltage((clockInputHigh && shouldSetEOCHigh[ch]) ? 10.f : 0.0f, ch); 696 //} 697 } 698 void setSyncTime(int channel, float time) { 699 //DEBUG("ch:%i,time:%f", channel, time); 700 syncTime[channel] = time; 701 } 702 void process(const ProcessArgs &args) override { 703 ComputerscarePolyModule::checkCounter(); 704 bool manualReset = globalManualResetTrigger.process(params[MANUAL_RESET_BUTTON].getValue()); 705 bool manualClock = clockManualTrigger.process(params[MANUAL_CLOCK_BUTTON].getValue()); 706 bool currentClock[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 707 708 bool currentReset[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 709 bool isHigh[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 710 711 float currentSyncTime; 712 for (int i = 0; i < 16; i++) { 713 currentClock[i] = manualClock || clockInputTrigger[i].process(inputs[CLOCK_INPUT].getVoltage(i)); 714 currentReset[i] = resetInputTrigger[i].process(inputs[RESET_INPUT].getVoltage(i)) || manualReset; 715 isHigh[i] = manualClock || clockInputTrigger[i].isHigh(); 716 717 currentSyncTime = syncTimer[i].process(args.sampleTime); 718 if (currentClock[i]) { 719 syncTimer[i].reset(); 720 setSyncTime(i, currentSyncTime); 721 } 722 } 723 724 if (mode == 0) { 725 //each poly channel processes independent trigger and cv 726 for (int i = 0; i < 16; i++) { 727 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1]); 728 } 729 } 730 else if (mode == 1) { 731 // all poly channels 2-16 CV only changes along with channel 1 trigger 732 // what to do with the triggers for these channels? 733 // force to 1 channel gate output? 734 for (int i = 0; i < 16; i++) { 735 if (i == 0) { 736 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1]); 737 } 738 else { 739 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1], mode, seqVal[0]); 740 } 741 } 742 } else if (mode == 2) { 743 // trigger cascade 744 for (int i = 0; i < 16; i++) { 745 if (i == 0) { 746 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1]); 747 } 748 else { 749 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1], mode, seqVal[i - 1]); 750 } 751 } 752 } 753 else if (mode == 3) { 754 // eoc cascade: previous channels EOC clocks next channels CV and trigger 755 for (int i = 0; i < 16; i++) { 756 if (i == 0) { 757 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1]); 758 } 759 else { 760 processChannel(i, currentClock[clockChannels[i] - 1], currentReset[resetChannels[i] - 1], isHigh[clockChannels[i] - 1], mode, shouldSetEOCHigh[i - 1]); 761 } 762 } 763 } 764 765 } 766 void checkPoly() override { 767 checkKnobChanges(); 768 } 769 void paramsFromJson(json_t* rootJ) override { 770 // There was no GATE_MODE param prior to v2, so set the value to 0 (clock passthrough) 771 setGateMode(0); 772 Module::paramsFromJson(rootJ); 773 } 774 }; 775 776 777 778 779 struct NumStepsOverKnobDisplay : SmallLetterDisplay 780 { 781 ComputerscareHorseADoodleDoo *module; 782 int knobConnection = 1; 783 NumStepsOverKnobDisplay(int type) 784 { 785 letterSpacing = 1.f; 786 knobConnection = type; 787 SmallLetterDisplay(); 788 }; 789 void draw(const DrawArgs &args) 790 { 791 if (module) 792 { 793 std::string str = ""; 794 if (knobConnection == 1) { 795 str = std::to_string(module->lastStepsKnob); 796 } 797 else if (knobConnection == 2) { 798 str = module->lastPolyKnob == 0 ? "A" : std::to_string(module->lastPolyKnob); 799 } 800 value = str; 801 } 802 else { 803 value = std::to_string((random::u32() % 64) + 1); 804 } 805 SmallLetterDisplay::draw(args); 806 } 807 }; 808 809 810 struct setModeItem : MenuItem 811 { 812 ComputerscareHorseADoodleDoo *horse; 813 int mySetVal; 814 setModeItem(int setVal) 815 { 816 mySetVal = setVal; 817 } 818 819 void onAction(const event::Action &e) override 820 { 821 horse->setMode(mySetVal); 822 } 823 void step() override { 824 rightText = CHECKMARK(horse->params[ComputerscareHorseADoodleDoo::MODE_KNOB].getValue() == mySetVal); 825 MenuItem::step(); 826 } 827 }; 828 struct setGateModeItem : MenuItem 829 { 830 ComputerscareHorseADoodleDoo *horse; 831 int mySetVal; 832 setGateModeItem(int setVal) 833 { 834 mySetVal = setVal; 835 } 836 837 void onAction(const event::Action &e) override 838 { 839 horse->setGateMode(mySetVal); 840 } 841 void step() override { 842 rightText = CHECKMARK(horse->params[ComputerscareHorseADoodleDoo::GATE_MODE].getValue() == mySetVal); 843 MenuItem::step(); 844 } 845 }; 846 847 struct ModeChildMenu : MenuItem { 848 ComputerscareHorseADoodleDoo *horse; 849 850 Menu *createChildMenu() override { 851 Menu *menu = new Menu; 852 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "How the polyphonic channels are triggered")); 853 854 for (unsigned int i = 0; i < 4; i++) { 855 setModeItem *menuItem = new setModeItem(i); 856 //ParamSettingItem *menuItem = new ParamSettingItem(i,ComputerscareGolyPenerator::ALGORITHM); 857 858 menuItem->text = HorseAvailableModes[i]; 859 menuItem->horse = horse; 860 menuItem->box.size.y = 40; 861 menu->addChild(menuItem); 862 } 863 864 return menu; 865 } 866 867 }; 868 struct GateModeChildMenu : MenuItem { 869 ComputerscareHorseADoodleDoo *horse; 870 871 Menu *createChildMenu() override { 872 Menu *menu = new Menu; 873 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Gate Output")); 874 875 for (unsigned int i = 0; i < 2; i++) { 876 setGateModeItem *menuItem = new setGateModeItem(i); 877 //ParamSettingItem *menuItem = new ParamSettingItem(i,ComputerscareGolyPenerator::ALGORITHM); 878 879 menuItem->text = HorseAvailableGateModes[i]; 880 menuItem->horse = horse; 881 menuItem->box.size.y = 40; 882 menu->addChild(menuItem); 883 } 884 885 return menu; 886 } 887 888 }; 889 890 struct ComputerscareHorseADoodleDooWidget : ModuleWidget { 891 ComputerscareHorseADoodleDooWidget(ComputerscareHorseADoodleDoo *module) { 892 893 setModule(module); 894 //setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareHorseADoodleDooPanel.svg"))); 895 box.size = Vec(6 * 15, 380); 896 { 897 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 898 panel->box.size = box.size; 899 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareHorseADoodleDooPanel.svg"))); 900 addChild(panel); 901 902 } 903 904 addInputBlock("Pattern", 10, 100, module, 0, ComputerscareHorseADoodleDoo::PATTERN_CV, 0, ComputerscareHorseADoodleDoo::PATTERN_SPREAD); 905 addInputBlock("Length", 10, 150, module, 2, ComputerscareHorseADoodleDoo::STEPS_CV, 1, ComputerscareHorseADoodleDoo::STEPS_SPREAD, false); 906 addInputBlock("Density", 10, 200, module, 4, ComputerscareHorseADoodleDoo::DENSITY_CV, 0, ComputerscareHorseADoodleDoo::DENSITY_SPREAD, false); 907 addParam(createParam<ScrambleSnapKnobNoRandom>(Vec(4, 234), module, ComputerscareHorseADoodleDoo::MODE_KNOB)); 908 909 /*for (int i = 0; i < 1; i++) { 910 horseDisplay = new HorseDisplay(i); 911 horseDisplay->module = module; 912 913 addChild(horseDisplay); 914 }*/ 915 916 int inputY = 264; 917 int outputY = 260; 918 919 920 921 int dy = 30; 922 923 int outputX = 42; 924 925 addParam(createParam<ComputerscareClockButton>(Vec(2, inputY - 6), module, ComputerscareHorseADoodleDoo::MANUAL_CLOCK_BUTTON)); 926 addInput(createInput<InPort>(Vec(2, inputY + 10), module, ComputerscareHorseADoodleDoo::CLOCK_INPUT)); 927 928 addParam(createParam<ComputerscareResetButton>(Vec(2, inputY + dy + 16), module, ComputerscareHorseADoodleDoo::MANUAL_RESET_BUTTON)); 929 930 addInput(createInput<InPort>(Vec(2, inputY + 2 * dy), module, ComputerscareHorseADoodleDoo::RESET_INPUT)); 931 932 933 channelWidget = new PolyOutputChannelsWidget(Vec(outputX + 18, inputY - 22), module, ComputerscareHorseADoodleDoo::POLY_KNOB); 934 addChild(channelWidget); 935 936 addOutput(createOutput<PointingUpPentagonPort>(Vec(outputX, outputY), module, ComputerscareHorseADoodleDoo::TRIGGER_OUTPUT)); 937 addOutput(createOutput<PointingUpPentagonPort>(Vec(outputX, outputY + dy), module, ComputerscareHorseADoodleDoo::EOC_OUTPUT)); 938 addOutput(createOutput<PointingUpPentagonPort>(Vec(outputX, outputY + dy * 2), module, ComputerscareHorseADoodleDoo::CV_OUTPUT)); 939 addOutput(createOutput<PointingUpPentagonPort>(Vec(outputX, outputY + dy * 3), module, ComputerscareHorseADoodleDoo::CV2_OUTPUT)); 940 941 } 942 943 944 void addInputBlock(std::string label, int x, int y, ComputerscareHorseADoodleDoo *module, int knobIndex, int inputIndex, int knobType, int scrambleIndex, bool allowScrambleRandom = true) { 945 946 smallLetterDisplay = new SmallLetterDisplay(); 947 smallLetterDisplay->box.size = Vec(5, 10); 948 smallLetterDisplay->letterSpacing = 0.5; 949 smallLetterDisplay->fontSize = 16; 950 smallLetterDisplay->value = label; 951 smallLetterDisplay->textAlign = 1; 952 smallLetterDisplay->box.pos = Vec(x - 4, y - 15); 953 954 955 if (knobType == 0) {//smooth 956 addParam(createParam<SmoothKnob>(Vec(x, y), module, knobIndex)); 957 //trim knob 958 959 960 } 961 else if (knobType == 1 || knobType == 2) { 962 numStepsKnob = new NumStepsOverKnobDisplay(knobType); 963 numStepsKnob->box.size = Vec(20, 20); 964 numStepsKnob->box.pos = Vec(x - 2.5 , y + 1.f); 965 numStepsKnob->fontSize = 26; 966 numStepsKnob->textAlign = 18; 967 numStepsKnob->textColor = COLOR_COMPUTERSCARE_LIGHT_GREEN; 968 numStepsKnob->breakRowWidth = 20; 969 numStepsKnob->module = module; 970 addParam(createParam<MediumDotSnapKnob>(Vec(x, y), module, knobIndex)); 971 addChild(numStepsKnob); 972 if (knobType == 1) { 973 //trim knob 974 //addParam(createParam<SmallKnob>(Vec(x + 30, y), module, knobIndex + 1)); 975 //addInput(createInput<TinyJack>(Vec(x + 40, y), module, inputIndex)); 976 //addParam(createParam<ScrambleKnob>(Vec(x+30, y+20), module, scrambleIndex)); 977 978 } 979 } 980 addParam(createParam<SmallKnob>(Vec(x + 32, y + 5), module, knobIndex + 1)); 981 addInput(createInput<TinyJack>(Vec(x + 54, y + 6), module, inputIndex)); 982 if (allowScrambleRandom) { 983 addParam(createParam<ScrambleKnob>(Vec(x + 55, y - 15), module, scrambleIndex)); 984 } 985 else { 986 addParam(createParam<ScrambleKnobNoRandom>(Vec(x + 55, y - 15), module, scrambleIndex)); 987 } 988 989 } 990 991 void appendContextMenu(Menu* menu) override { 992 ComputerscareHorseADoodleDoo* horse = dynamic_cast<ComputerscareHorseADoodleDoo*>(this->module); 993 994 struct CV1Submenu : MenuItem { 995 ComputerscareHorseADoodleDoo* module; 996 Menu *createChildMenu() override { 997 Menu *submenu = new Menu; 998 999 submenu->addChild(construct<MenuLabel>(&MenuLabel::text, "Configuration of the 1st Control Voltage (CV) Pattern")); 1000 1001 MenuParam* cvScaleParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV_SCALE], 2); 1002 submenu->addChild(cvScaleParamControl); 1003 1004 MenuParam* cvOffsetParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV_OFFSET], 2); 1005 submenu->addChild(cvOffsetParamControl); 1006 1007 MenuParam* cvPhaseParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV_PHASE], 2); 1008 submenu->addChild(cvPhaseParamControl); 1009 1010 return submenu; 1011 } 1012 }; 1013 struct CV2Submenu : MenuItem { 1014 ComputerscareHorseADoodleDoo* module; 1015 Menu *createChildMenu() override { 1016 Menu *submenu = new Menu; 1017 1018 submenu->addChild(construct<MenuLabel>(&MenuLabel::text, "Configuration of the 2nd Control Voltage (CV2) Pattern")); 1019 1020 MenuParam* cvScaleParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV2_SCALE], 2); 1021 submenu->addChild(cvScaleParamControl); 1022 1023 MenuParam* cvOffsetParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV2_OFFSET], 2); 1024 submenu->addChild(cvOffsetParamControl); 1025 1026 MenuParam* cvPhaseParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::CV2_PHASE], 2); 1027 submenu->addChild(cvPhaseParamControl); 1028 1029 return submenu; 1030 } 1031 }; 1032 struct GateLengthSubmenu : MenuItem { 1033 ComputerscareHorseADoodleDoo* module; 1034 Menu *createChildMenu() override { 1035 Menu *submenu = new Menu; 1036 1037 submenu->addChild(construct<MenuLabel>(&MenuLabel::text, "Configuration of the Pattern of Gate Lengths")); 1038 MenuParam* gateScaleParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::GATE_LENGTH_SCALE], 2); 1039 submenu->addChild(gateScaleParamControl); 1040 1041 MenuParam* gateOffsetParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::GATE_LENGTH_OFFSET], 2); 1042 submenu->addChild(gateOffsetParamControl); 1043 1044 MenuParam* gatePhaseParamControl = new MenuParam(module->paramQuantities[ComputerscareHorseADoodleDoo::GATE_LENGTH_PHASE], 2); 1045 submenu->addChild(gatePhaseParamControl); 1046 1047 return submenu; 1048 } 1049 }; 1050 1051 menu->addChild(new MenuEntry); 1052 ModeChildMenu *modeMenu = new ModeChildMenu(); 1053 modeMenu->text = "Polyphonic Triggering Mode"; 1054 modeMenu->rightText = RIGHT_ARROW; 1055 modeMenu->horse = horse; 1056 menu->addChild(modeMenu); 1057 1058 GateModeChildMenu *gateModeMenu = new GateModeChildMenu(); 1059 gateModeMenu->text = "Gate Output Mode"; 1060 gateModeMenu->rightText = RIGHT_ARROW; 1061 gateModeMenu->horse = horse; 1062 menu->addChild(gateModeMenu); 1063 1064 menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); 1065 1066 CV1Submenu *cv1 = new CV1Submenu(); 1067 cv1->text = "CV 1 Configuration"; 1068 cv1->rightText = RIGHT_ARROW; 1069 cv1->module = horse; 1070 menu->addChild(cv1); 1071 1072 1073 CV2Submenu *cv2 = new CV2Submenu(); 1074 cv2->text = "CV 2 Configuration"; 1075 cv2->rightText = RIGHT_ARROW; 1076 cv2->module = horse; 1077 menu->addChild(cv2); 1078 1079 1080 1081 GateLengthSubmenu *gateMenu = new GateLengthSubmenu(); 1082 gateMenu->text = "Gate Length Configuration"; 1083 gateMenu->rightText = RIGHT_ARROW; 1084 gateMenu->module = horse; 1085 menu->addChild(gateMenu); 1086 1087 } 1088 PolyOutputChannelsWidget* channelWidget; 1089 NumStepsOverKnobDisplay* numStepsKnob; 1090 SmallLetterDisplay* smallLetterDisplay; 1091 }; 1092 1093 Model *modelComputerscareHorseADoodleDoo = createModel<ComputerscareHorseADoodleDoo, ComputerscareHorseADoodleDooWidget>("computerscare-horse-a-doodle-doo");