BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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");