| 111 | |
| 112 | |
| 113 | void process(const ProcessArgs& args) override { |
| 114 | |
| 115 | const int numActivePolyphonyEngines = getNumActivePolyphonyEngines(); |
| 116 | |
| 117 | // work out active outputs |
| 118 | const int highestOutput = getMaxConnectedOutput(); |
| 119 | if (highestOutput == -1) { |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | for (int c = 0; c < numActivePolyphonyEngines; c += 4) { |
| 124 | |
| 125 | const int rangeIndex = params[RANGE_PARAM].getValue(); |
| 126 | float_4 pitch = ranges[rangeIndex] * params[TUNE_PARAM].getValue() + inputs[VOCT1_INPUT].getPolyVoltageSimd<float_4>(c) + inputs[VOCT2_INPUT].getPolyVoltageSimd<float_4>(c); |
| 127 | pitch += params[OCTAVE_PARAM].getValue() - 3; |
| 128 | const float_4 freq = dsp::FREQ_C4 * dsp::exp2_taylor5(pitch); |
| 129 | // -1 to +1 |
| 130 | const float_4 pwmCV = params[PWM_CV_PARAM].getValue() * clamp(inputs[PWM_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f, -1.f, 1.f); |
| 131 | const float_4 pulseWidthLimit = limitPW ? 0.05f : 0.0f; |
| 132 | |
| 133 | // pwm in [-0.25 : +0.25] |
| 134 | const float_4 pwm = 2 * clamp(0.5 - params[PWM_PARAM].getValue() + 0.5 * pwmCV, -0.5f + pulseWidthLimit, 0.5f - pulseWidthLimit); |
| 135 | |
| 136 | const int oversamplingRatio = oversampler[0][0].getOversamplingRatio(); |
| 137 | |
| 138 | const float_4 deltaPhase = freq * args.sampleTime / oversamplingRatio; |
| 139 | |
| 140 | // process sync |
| 141 | float_4 sync = syncTrigger[c / 4].process(inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c)); |
| 142 | phase[c / 4] = simd::ifelse(sync, 0.5f, phase[c / 4]); |
| 143 | |
| 144 | |
| 145 | for (int i = 0; i < oversamplingRatio; i++) { |
| 146 | |
| 147 | phase[c / 4] += deltaPhase; |
| 148 | phase[c / 4] -= simd::floor(phase[c / 4]); |
| 149 | |
| 150 | float_4 sum = {}; |
| 151 | for (int oct = 0; oct <= highestOutput; oct++) { |
| 152 | |
| 153 | const float_4 gainCV = simd::clamp(inputs[GAIN_01F_INPUT + oct].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f, 0.f, 1.0f); |
| 154 | const float_4 gain = params[GAIN_01F_PARAM + oct].getValue() * gainCV; |
| 155 | |
| 156 | // don't bother processing if gain is zero and no output is connected |
| 157 | const bool isGainZero = simd::movemask(gain != 0.f) == 0; |
| 158 | if (isGainZero && !outputs[OUT_01F_OUTPUT + oct].isConnected()) { |
| 159 | continue; |
| 160 | } |
| 161 | |
| 162 | // derive phases for higher octaves from base phase (this keeps things in sync!) |
| 163 | const float_4 n = (float)(1 << oct); |
| 164 | // this is on [0, 1] |
| 165 | const float_4 effectivePhase = n * simd::fmod(phase[c / 4], 1 / n); |
| 166 | const float_4 waveTri = 1.0 - 2.0 * simd::abs(2.f * effectivePhase - 1.0); |
| 167 | // build square from triangle + comparator |
| 168 | const float_4 waveSquare = simd::ifelse(waveTri > pwm, +1.f, -1.f); |
| 169 | |
| 170 | sum += (useTriangleCore ? waveTri : waveSquare) * gain; |
nothing calls this directly
no test coverage detected