| 413 | } |
| 414 | |
| 415 | void process(const ProcessArgs& args) override { |
| 416 | |
| 417 | usingExternalClock = inputs[CLOCK_INPUT].isConnected(); |
| 418 | |
| 419 | bool externalClockPulseReceived = false; |
| 420 | // a clock pulse does two things: 1) sets the internal clock (based on timing between two pulses), which |
| 421 | // would continue were the clock input to be removed, and 2) synchronises/drives the clock (if clock input present) |
| 422 | if (usingExternalClock && inputClockTrigger.process(rescale(inputs[CLOCK_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) { |
| 423 | externalClockPulseReceived = true; |
| 424 | } |
| 425 | // this can also be sent by tap tempo |
| 426 | else if (!usingExternalClock && tapTempoTrigger.process(tapped)) { |
| 427 | externalClockPulseReceived = true; |
| 428 | tapped = false; |
| 429 | } |
| 430 | |
| 431 | mainClockMultDiv.multDiv = getClockOptionFromParam(); |
| 432 | |
| 433 | processPlayResetLogic(); |
| 434 | |
| 435 | const float address = params[ADDRESS_PARAM].getValue() + inputs[ADDRESS_INPUT].getVoltage(); |
| 436 | const bool isAddressInRunMode = address < 0.f; |
| 437 | |
| 438 | // even if we have an external clock, use its pulses to time/sync the internal clock |
| 439 | // so that it will remain running even after CLOCK_INPUT is disconnected |
| 440 | if (externalClockPulseReceived) { |
| 441 | // track length between received clock pulses (using external clock) or taps |
| 442 | // of the tap-tempo menu item (if sufficiently short) |
| 443 | if (usingExternalClock || tapTime < 2.f) { |
| 444 | internalClockLength = tapTime; |
| 445 | } |
| 446 | tapTime = 0; |
| 447 | internalClockProgress = 0.f; |
| 448 | } |
| 449 | |
| 450 | // If we get a reset signal (which can come from CV or various modes of the switch), and the clock has only |
| 451 | // just started to tick (internalClockProgress < 1ms), we assume that the reset signal is slightly delayed |
| 452 | // due to the 1 sample delay that Rack introduces. If this is the case, the internal clock trigger detector, |
| 453 | // `detectResetTrigger`, which advances the sequence, will not be "primed" to detect a rising edge for another |
| 454 | // whole clock tick, meaning the first step is repeated. See: https://github.com/VCVRack/Befaco/issues/32 |
| 455 | // Also see https://vcvrack.com/manual/VoltageStandards#Timing for 0.001 seconds justification. |
| 456 | if (detectResetTrigger.process(resetRequested != RESET_NOT_REQUESTED) && internalClockProgress < 1e-3) { |
| 457 | // NOTE: the sequence must also be stopped for this to come into effect. In hardware, if the Nth step Gate Out |
| 458 | // is patched back into the reset, that step should complete before the sequence restarts. |
| 459 | if (playState == STATE_STOPPED) { |
| 460 | mainClockTrigger.state = false; |
| 461 | } |
| 462 | } |
| 463 | tapTime += args.sampleTime; |
| 464 | internalClockProgress += args.sampleTime; |
| 465 | |
| 466 | // track if the internal clock has "ticked" |
| 467 | const bool internalClockPulseReceived = (internalClockProgress >= internalClockLength); |
| 468 | if (internalClockPulseReceived) { |
| 469 | internalClockProgress = 0.f; |
| 470 | } |
| 471 | |
| 472 | // we can be in one of two clock modes: |
nothing calls this directly
no test coverage detected