EightFO.cpp (13732B)
1 2 #include "EightFO.hpp" 3 4 const Phasor::phase_delta_t basePhase7Offset = Phasor::radiansToPhase(1.75f * M_PI); 5 const Phasor::phase_delta_t basePhase6Offset = Phasor::radiansToPhase(1.5f * M_PI); 6 const Phasor::phase_delta_t basePhase5Offset = Phasor::radiansToPhase(1.25f * M_PI); 7 const Phasor::phase_delta_t basePhase4Offset = Phasor::radiansToPhase(M_PI); 8 const Phasor::phase_delta_t basePhase3Offset = Phasor::radiansToPhase(0.75f * M_PI); 9 const Phasor::phase_delta_t basePhase2Offset = Phasor::radiansToPhase(0.5f * M_PI); 10 const Phasor::phase_delta_t basePhase1Offset = Phasor::radiansToPhase(0.25f * M_PI); 11 const Phasor::phase_delta_t basePhase0Offset = Phasor::radiansToPhase(0.0f); 12 13 void EightFO::Engine::reset() { 14 resetTrigger.reset(); 15 sampleStep = phasor._sampleRate; 16 } 17 18 void EightFO::Engine::sampleRateChange() { 19 phasor.setSampleRate(APP->engine->getSampleRate()); 20 sampleStep = phasor._sampleRate; 21 } 22 23 void EightFO::reset() { 24 for (int c = 0; c < _channels; ++c) { 25 _engines[c]->reset(); 26 } 27 } 28 29 void EightFO::sampleRateChange() { 30 for (int c = 0; c < _channels; ++c) { 31 _engines[c]->sampleRateChange(); 32 } 33 } 34 35 bool EightFO::active() { 36 return ( 37 outputs[PHASE7_OUTPUT].isConnected() || 38 outputs[PHASE6_OUTPUT].isConnected() || 39 outputs[PHASE5_OUTPUT].isConnected() || 40 outputs[PHASE4_OUTPUT].isConnected() || 41 outputs[PHASE3_OUTPUT].isConnected() || 42 outputs[PHASE2_OUTPUT].isConnected() || 43 outputs[PHASE1_OUTPUT].isConnected() || 44 outputs[PHASE0_OUTPUT].isConnected() 45 ); 46 } 47 48 int EightFO::channels() { 49 return inputs[PITCH_INPUT].getChannels(); 50 } 51 52 void EightFO::addChannel(int c) { 53 _engines[c] = new Engine(); 54 _engines[c]->reset(); 55 _engines[c]->sampleRateChange(); 56 if (c > 0) { 57 _engines[c]->phasor.syncPhase(_engines[0]->phasor); 58 } 59 } 60 61 void EightFO::removeChannel(int c) { 62 delete _engines[c]; 63 _engines[c] = NULL; 64 } 65 66 void EightFO::modulate() { 67 _wave = (Wave)roundf(params[WAVE_PARAM].getValue()); 68 _slowMode = params[SLOW_PARAM].getValue() > 0.5f; 69 } 70 71 void EightFO::modulateChannel(int c) { 72 Engine& e = *_engines[c]; 73 74 setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], e.phasor, c); 75 76 switch (_wave) { 77 case SQUARE_WAVE: { 78 float pw = params[SAMPLE_PWM_PARAM].getValue(); 79 if (inputs[SAMPLE_PWM_INPUT].isConnected()) { 80 pw *= clamp(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); 81 } 82 pw *= 1.0f - 2.0f * e.square.minPulseWidth; 83 pw *= 0.5f; 84 pw += 0.5f; 85 e.square.setPulseWidth(pw); 86 e.sampleSteps = 1; 87 break; 88 } 89 case STEPPED_WAVE: { 90 e.sampleSteps = 1; 91 break; 92 } 93 default: { 94 float sample = fabsf(params[SAMPLE_PWM_PARAM].getValue()); 95 if (inputs[SAMPLE_PWM_INPUT].isConnected()) { 96 sample *= clamp(fabsf(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c)) / 5.0f, 0.0f, 1.0f); 97 } 98 float maxSampleSteps = (e.phasor._sampleRate / e.phasor._frequency) / 4.0f; 99 e.sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); 100 } 101 } 102 103 float smooth = params[SMOOTH_PARAM].getValue(); 104 if (inputs[SMOOTH_INPUT].isConnected()) { 105 smooth *= clamp(inputs[SMOOTH_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); 106 } 107 float sr = APP->engine->getSampleRate(); 108 e.phase7Smoother.setParams(sr, e.phasor._frequency, smooth); 109 e.phase6Smoother.setParams(sr, e.phasor._frequency, smooth); 110 e.phase5Smoother.setParams(sr, e.phasor._frequency, smooth); 111 e.phase4Smoother.setParams(sr, e.phasor._frequency, smooth); 112 e.phase3Smoother.setParams(sr, e.phasor._frequency, smooth); 113 e.phase2Smoother.setParams(sr, e.phasor._frequency, smooth); 114 e.phase1Smoother.setParams(sr, e.phasor._frequency, smooth); 115 e.phase0Smoother.setParams(sr, e.phasor._frequency, smooth); 116 117 e.offset = params[OFFSET_PARAM].getValue(); 118 if (inputs[OFFSET_INPUT].isConnected()) { 119 e.offset *= clamp(inputs[OFFSET_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); 120 } 121 e.offset *= _offsetScale; 122 e.offset *= 5.0f; 123 e.scale = params[SCALE_PARAM].getValue(); 124 if (inputs[SCALE_INPUT].isConnected()) { 125 e.scale *= clamp(inputs[SCALE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); 126 } 127 128 e.phase7Offset = phaseOffset(c, params[PHASE7_PARAM], inputs[PHASE7_INPUT], basePhase7Offset); 129 e.phase6Offset = phaseOffset(c, params[PHASE6_PARAM], inputs[PHASE6_INPUT], basePhase6Offset); 130 e.phase5Offset = phaseOffset(c, params[PHASE5_PARAM], inputs[PHASE5_INPUT], basePhase5Offset); 131 e.phase4Offset = phaseOffset(c, params[PHASE4_PARAM], inputs[PHASE4_INPUT], basePhase4Offset); 132 e.phase3Offset = phaseOffset(c, params[PHASE3_PARAM], inputs[PHASE3_INPUT], basePhase3Offset); 133 e.phase2Offset = phaseOffset(c, params[PHASE2_PARAM], inputs[PHASE2_INPUT], basePhase2Offset); 134 e.phase1Offset = phaseOffset(c, params[PHASE1_PARAM], inputs[PHASE1_INPUT], basePhase1Offset); 135 e.phase0Offset = phaseOffset(c, params[PHASE0_PARAM], inputs[PHASE0_INPUT], basePhase0Offset); 136 } 137 138 void EightFO::processChannel(const ProcessArgs& args, int c) { 139 Engine& e = *_engines[c]; 140 141 if (e.resetTrigger.next(inputs[RESET_INPUT].getPolyVoltage(c))) { 142 e.phasor.resetPhase(); 143 } 144 145 e.phasor.advancePhase(); 146 bool useSample = false; 147 if (e.sampleSteps > 1) { 148 ++e.sampleStep; 149 if (e.sampleStep >= e.sampleSteps) { 150 e.sampleStep = 0; 151 } 152 else { 153 useSample = true; 154 } 155 } 156 updateOutput(c, useSample, outputs[PHASE7_OUTPUT], e.phase7Offset, e.phase7Sample, e.phase7Active, e.phase7Smoother); 157 updateOutput(c, useSample, outputs[PHASE6_OUTPUT], e.phase6Offset, e.phase6Sample, e.phase6Active, e.phase6Smoother); 158 updateOutput(c, useSample, outputs[PHASE5_OUTPUT], e.phase5Offset, e.phase5Sample, e.phase5Active, e.phase5Smoother); 159 updateOutput(c, useSample, outputs[PHASE4_OUTPUT], e.phase4Offset, e.phase4Sample, e.phase4Active, e.phase4Smoother); 160 updateOutput(c, useSample, outputs[PHASE3_OUTPUT], e.phase3Offset, e.phase3Sample, e.phase3Active, e.phase3Smoother); 161 updateOutput(c, useSample, outputs[PHASE2_OUTPUT], e.phase2Offset, e.phase2Sample, e.phase2Active, e.phase2Smoother); 162 updateOutput(c, useSample, outputs[PHASE1_OUTPUT], e.phase1Offset, e.phase1Sample, e.phase1Active, e.phase1Smoother); 163 updateOutput(c, useSample, outputs[PHASE0_OUTPUT], e.phase0Offset, e.phase0Sample, e.phase0Active, e.phase0Smoother); 164 } 165 166 Phasor::phase_delta_t EightFO::phaseOffset(int c, Param& p, Input& i, Phasor::phase_delta_t baseOffset) { 167 float o = p.getValue() * Phasor::cyclePhase / 2.0f; 168 if (i.isConnected()) { 169 o *= clamp(i.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); 170 } 171 return baseOffset - o; 172 } 173 174 void EightFO::updateOutput(int c, bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active, Smoother& smoother) { 175 if (output.isConnected()) { 176 output.setChannels(_channels); 177 if (!useSample || !active) { 178 float v = 0.0f; 179 switch (_wave) { 180 case NO_WAVE: { 181 assert(false); 182 } 183 case SINE_WAVE: { 184 v = _engines[c]->sine.nextFromPhasor(_engines[c]->phasor, offset); 185 break; 186 } 187 case TRIANGLE_WAVE: { 188 v = _engines[c]->triangle.nextFromPhasor(_engines[c]->phasor, offset); 189 break; 190 } 191 case RAMP_UP_WAVE: { 192 v = _engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset); 193 break; 194 } 195 case RAMP_DOWN_WAVE: { 196 v = -_engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset); 197 break; 198 } 199 case SQUARE_WAVE: { 200 v = _engines[c]->square.nextFromPhasor(_engines[c]->phasor, offset); 201 break; 202 } 203 case STEPPED_WAVE: { 204 v = _engines[c]->stepped.nextFromPhasor(_engines[c]->phasor, offset); 205 break; 206 } 207 } 208 sample = amplitude * _engines[c]->scale * v + _engines[c]->offset; 209 } 210 output.setVoltage(clamp(smoother.next(sample), -12.0f, 12.0f), c); 211 active = true; 212 } 213 else { 214 active = false; 215 } 216 } 217 218 struct EightFOWidget : LFOBaseModuleWidget { 219 static constexpr int hp = 17; 220 221 EightFOWidget(EightFO* module) { 222 setModule(module); 223 box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); 224 setPanel(box.size, "EightFO"); 225 createScrews(); 226 227 // generated by svg_widgets.rb 228 auto frequencyParamPosition = Vec(40.0, 45.0); 229 auto slowParamPosition = Vec(50.0, 139.7); 230 auto waveParamPosition = Vec(20.0, 178.0); 231 auto samplePwmParamPosition = Vec(100.0, 143.0); 232 auto smoothParamPosition = Vec(100.0, 190.0); 233 auto offsetParamPosition = Vec(40.0, 235.0); 234 auto scaleParamPosition = Vec(100.0, 235.0); 235 auto phase7ParamPosition = Vec(144.0, 40.0); 236 auto phase6ParamPosition = Vec(144.0, 80.0); 237 auto phase5ParamPosition = Vec(144.0, 120.0); 238 auto phase4ParamPosition = Vec(144.0, 160.0); 239 auto phase3ParamPosition = Vec(144.0, 200.0); 240 auto phase2ParamPosition = Vec(144.0, 240.0); 241 auto phase1ParamPosition = Vec(144.0, 280.0); 242 auto phase0ParamPosition = Vec(144.0, 320.0); 243 244 auto phase7InputPosition = Vec(179.0, 36.0); 245 auto phase6InputPosition = Vec(179.0, 76.0); 246 auto phase5InputPosition = Vec(179.0, 116.0); 247 auto phase4InputPosition = Vec(179.0, 156.0); 248 auto phase3InputPosition = Vec(179.0, 196.0); 249 auto phase2InputPosition = Vec(179.0, 236.0); 250 auto phase1InputPosition = Vec(179.0, 276.0); 251 auto phase0InputPosition = Vec(179.0, 316.0); 252 auto samplePwmInputPosition = Vec(21.0, 277.0); 253 auto offsetInputPosition = Vec(59.0, 277.0); 254 auto scaleInputPosition = Vec(97.0, 277.0); 255 auto pitchInputPosition = Vec(21.0, 318.0); 256 auto resetInputPosition = Vec(59.0, 318.0); 257 auto smoothInputPosition = Vec(97.0, 318.0); 258 259 auto phase7OutputPosition = Vec(218.0, 36.0); 260 auto phase6OutputPosition = Vec(218.0, 76.0); 261 auto phase5OutputPosition = Vec(218.0, 116.0); 262 auto phase4OutputPosition = Vec(218.0, 156.0); 263 auto phase3OutputPosition = Vec(218.0, 196.0); 264 auto phase2OutputPosition = Vec(218.0, 236.0); 265 auto phase1OutputPosition = Vec(218.0, 276.0); 266 auto phase0OutputPosition = Vec(218.0, 316.0); 267 // end generated by svg_widgets.rb 268 269 addParam(createParam<Knob68>(frequencyParamPosition, module, EightFO::FREQUENCY_PARAM)); 270 { 271 auto w = createParam<Knob16>(waveParamPosition, module, EightFO::WAVE_PARAM); 272 auto k = dynamic_cast<SvgKnob*>(w); 273 k->minAngle = 0.0; 274 k->maxAngle = M_PI; 275 k->speed = 3.0; 276 addParam(w); 277 } 278 addParam(createParam<IndicatorButtonGreen9>(slowParamPosition, module, EightFO::SLOW_PARAM)); 279 addParam(createParam<Knob26>(samplePwmParamPosition, module, EightFO::SAMPLE_PWM_PARAM)); 280 addParam(createParam<Knob26>(smoothParamPosition, module, EightFO::SMOOTH_PARAM)); 281 addParam(createParam<Knob26>(offsetParamPosition, module, EightFO::OFFSET_PARAM)); 282 addParam(createParam<Knob26>(scaleParamPosition, module, EightFO::SCALE_PARAM)); 283 284 addPhaseParam(phase7ParamPosition, module, EightFO::PHASE7_PARAM, Phasor::phaseToRadians(basePhase7Offset)); 285 addPhaseParam(phase6ParamPosition, module, EightFO::PHASE6_PARAM, Phasor::phaseToRadians(basePhase6Offset)); 286 addPhaseParam(phase5ParamPosition, module, EightFO::PHASE5_PARAM, Phasor::phaseToRadians(basePhase5Offset)); 287 addPhaseParam(phase4ParamPosition, module, EightFO::PHASE4_PARAM, Phasor::phaseToRadians(basePhase4Offset)); 288 addPhaseParam(phase3ParamPosition, module, EightFO::PHASE3_PARAM, Phasor::phaseToRadians(basePhase3Offset)); 289 addPhaseParam(phase2ParamPosition, module, EightFO::PHASE2_PARAM, Phasor::phaseToRadians(basePhase2Offset)); 290 addPhaseParam(phase1ParamPosition, module, EightFO::PHASE1_PARAM, Phasor::phaseToRadians(basePhase1Offset)); 291 addPhaseParam(phase0ParamPosition, module, EightFO::PHASE0_PARAM, Phasor::phaseToRadians(basePhase0Offset)); 292 293 addInput(createInput<Port24>(samplePwmInputPosition, module, EightFO::SAMPLE_PWM_INPUT)); 294 addInput(createInput<Port24>(offsetInputPosition, module, EightFO::OFFSET_INPUT)); 295 addInput(createInput<Port24>(scaleInputPosition, module, EightFO::SCALE_INPUT)); 296 addInput(createInput<Port24>(phase7InputPosition, module, EightFO::PHASE7_INPUT)); 297 addInput(createInput<Port24>(phase6InputPosition, module, EightFO::PHASE6_INPUT)); 298 addInput(createInput<Port24>(phase5InputPosition, module, EightFO::PHASE5_INPUT)); 299 addInput(createInput<Port24>(phase4InputPosition, module, EightFO::PHASE4_INPUT)); 300 addInput(createInput<Port24>(phase3InputPosition, module, EightFO::PHASE3_INPUT)); 301 addInput(createInput<Port24>(phase2InputPosition, module, EightFO::PHASE2_INPUT)); 302 addInput(createInput<Port24>(phase1InputPosition, module, EightFO::PHASE1_INPUT)); 303 addInput(createInput<Port24>(phase0InputPosition, module, EightFO::PHASE0_INPUT)); 304 addInput(createInput<Port24>(pitchInputPosition, module, EightFO::PITCH_INPUT)); 305 addInput(createInput<Port24>(resetInputPosition, module, EightFO::RESET_INPUT)); 306 addInput(createInput<Port24>(smoothInputPosition, module, EightFO::SMOOTH_INPUT)); 307 308 addOutput(createOutput<Port24>(phase7OutputPosition, module, EightFO::PHASE7_OUTPUT)); 309 addOutput(createOutput<Port24>(phase6OutputPosition, module, EightFO::PHASE6_OUTPUT)); 310 addOutput(createOutput<Port24>(phase5OutputPosition, module, EightFO::PHASE5_OUTPUT)); 311 addOutput(createOutput<Port24>(phase4OutputPosition, module, EightFO::PHASE4_OUTPUT)); 312 addOutput(createOutput<Port24>(phase3OutputPosition, module, EightFO::PHASE3_OUTPUT)); 313 addOutput(createOutput<Port24>(phase2OutputPosition, module, EightFO::PHASE2_OUTPUT)); 314 addOutput(createOutput<Port24>(phase1OutputPosition, module, EightFO::PHASE1_OUTPUT)); 315 addOutput(createOutput<Port24>(phase0OutputPosition, module, EightFO::PHASE0_OUTPUT)); 316 } 317 318 void addPhaseParam(const Vec& position, Module* module, EightFO::ParamsIds paramId, float rotation) { 319 auto w = createParam<Knob16>(position, module, paramId); 320 auto k = dynamic_cast<SvgKnob*>(w); 321 k->minAngle += 0.5 * M_PI - rotation; 322 k->maxAngle += 0.5 * M_PI - rotation; 323 addParam(w); 324 } 325 }; 326 327 Model* modelEightFO = bogaudio::createModel<EightFO, EightFOWidget>("Bogaudio-EightFO", "8FO", "LFO with outputs at 8 different phases", "LFO", "Random", "Polyphonic");