BogaudioModules

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

RGate.cpp (9111B)


      1 
      2 #include "RGate.hpp"
      3 
      4 #define RESET_MODE "reset_mode"
      5 #define INITIAL_CLOCK_SECONDS "initial_clock_seconds"
      6 #define POLY_INPUT "poly_input"
      7 
      8 void RGate::Engine::reset(bool triggers, bool hard, float initialClock) {
      9 	if (triggers) {
     10 		clockTrigger.reset();
     11 		runTrigger.reset();
     12 		initialGatePulseGen.process(10.0f);
     13 	}
     14 	if (hard) {
     15 		secondsSinceLastClock = -1.0f;
     16 		clockSeconds = initialClock;
     17 		dividedSeconds = -1.0f;
     18 		multipliedSeconds = -1.0f;
     19 		gateSeconds = 0.0f;
     20 	}
     21 	dividerCount = 0;
     22 	dividedProgressSeconds = 0.0f;
     23 }
     24 
     25 void RGate::reset() {
     26 	for (int c = 0; c < _channels; ++c) {
     27 		_engines[c]->reset(true, true, _initialClockPeriod);
     28 	}
     29 }
     30 
     31 void RGate::sampleRateChange() {
     32 	_sampleTime = APP->engine->getSampleTime();
     33 }
     34 
     35 json_t* RGate::saveToJson(json_t* root) {
     36 	root = OutputRangeModule<BGModule>::saveToJson(root);
     37 	json_object_set_new(root, RESET_MODE, json_integer(_resetMode));
     38 	json_object_set_new(root, INITIAL_CLOCK_SECONDS, json_real(_initialClockPeriod));
     39 	json_object_set_new(root, POLY_INPUT, json_integer(_polyInputID));
     40 	return root;
     41 }
     42 
     43 void RGate::loadFromJson(json_t* root) {
     44 	OutputRangeModule<BGModule>::loadFromJson(root);
     45 
     46 	json_t* rm = json_object_get(root, RESET_MODE);
     47 	if (rm) {
     48 		ResetMode m = (ResetMode)json_integer_value(rm);
     49 		switch (m) {
     50 			case HARD_RESETMODE:
     51 			case SOFT_RESETMODE: {
     52 				_resetMode = m;
     53 				break;
     54 			}
     55 			default: {
     56 				_resetMode = defaultResetMode;
     57 			}
     58 		}
     59 	}
     60 
     61 	json_t* ics = json_object_get(root, INITIAL_CLOCK_SECONDS);
     62 	if (ics) {
     63 		_initialClockPeriod = std::max(0.0f, (float)json_real_value(ics));
     64 	}
     65 
     66 	json_t* p = json_object_get(root, POLY_INPUT);
     67 	if (p) {
     68 		_polyInputID = json_integer_value(p);
     69 	}
     70 }
     71 
     72 bool RGate::active() {
     73 	return outputs[GATE_OUTPUT].isConnected();
     74 }
     75 
     76 int RGate::channels() {
     77 	return inputs[_polyInputID].getChannels();
     78 }
     79 
     80 void RGate::addChannel(int c) {
     81 	_engines[c] = new Engine();
     82 	_engines[c]->reset(true, true, _initialClockPeriod);
     83 }
     84 
     85 void RGate::removeChannel(int c) {
     86 	delete _engines[c];
     87 	_engines[c] = NULL;
     88 }
     89 
     90 void RGate::modulateChannel(int c) {
     91 	Engine& e = *_engines[c];
     92 
     93 	e.gatePercentage = clamp(params[LENGTH_PARAM].getValue(), 0.0f, 1.0f);
     94 	if (inputs[LENGTH_INPUT].isConnected()) {
     95 		e.gatePercentage *= clamp(inputs[LENGTH_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
     96 	}
     97 
     98 	float division = clamp(params[CLOCK_DIVIDE_PARAM].getValue(), 0.0f, 1.0f);
     99 	if (inputs[CLOCK_DIVIDE_INPUT].isConnected()) {
    100 		division *= clamp(inputs[CLOCK_DIVIDE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
    101 	}
    102 	division *= division;
    103 	division *= 63.0f;
    104 	division += 1.0f;
    105 	e.division = clamp((int)roundf(division), 1, 64);
    106 
    107 	float multiplication = clamp(params[CLOCK_MULTIPLY_PARAM].getValue(), 0.0f, 1.0f);
    108 	if (inputs[CLOCK_MULTIPLE_INPUT].isConnected()) {
    109 		multiplication *= clamp(inputs[CLOCK_MULTIPLE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
    110 	}
    111 	multiplication *= multiplication;
    112 	multiplication *= 63.0f;
    113 	multiplication += 1.0f;
    114 	e.multiplication = clamp((int)roundf(multiplication), 1, 64);
    115 }
    116 
    117 void RGate::processChannel(const ProcessArgs& args, int c) {
    118 	Engine& e = *_engines[c];
    119 
    120 	if (e.runTrigger.process(inputs[RESET_INPUT].getPolyVoltage(c))) {
    121 		switch (_resetMode) {
    122 			case HARD_RESETMODE: {
    123 				e.reset(false, true, _initialClockPeriod);
    124 				break;
    125 			}
    126 			case SOFT_RESETMODE: {
    127 				e.reset(false, false, _initialClockPeriod);
    128 				break;
    129 			}
    130 		}
    131 	}
    132 
    133 	float out = -1.0f;
    134 	bool clock = false;
    135 	if (inputs[CLOCK_INPUT].isConnected()) {
    136 		clock = e.clockTrigger.process(inputs[CLOCK_INPUT].getPolyVoltage(c));
    137 		if (clock) {
    138 			if (e.secondsSinceLastClock > 0.0f) {
    139 				e.clockSeconds = e.secondsSinceLastClock;
    140 			}
    141 			e.secondsSinceLastClock = 0.0f;
    142 		}
    143 
    144 		if (e.secondsSinceLastClock >= 0.0f) {
    145 			e.secondsSinceLastClock += _sampleTime;
    146 			e.dividedSeconds = e.clockSeconds * (float)e.division;
    147 			e.multipliedSeconds = e.dividedSeconds / (float)e.multiplication;
    148 			e.gateSeconds = std::max(0.001f, e.multipliedSeconds * e.gatePercentage);
    149 			if (clock) {
    150 				if (e.dividerCount < 1) {
    151 					e.dividedProgressSeconds = 0.0f;
    152 				}
    153 				else {
    154 					e.dividedProgressSeconds += _sampleTime;
    155 				}
    156 				++e.dividerCount;
    157 				if (e.dividerCount >= e.division) {
    158 					e.dividerCount = 0;
    159 				}
    160 			}
    161 			else {
    162 				e.dividedProgressSeconds += _sampleTime;
    163 			}
    164 
    165 			if (e.dividedProgressSeconds < e.dividedSeconds) {
    166 				float multipliedProgressSeconds = e.dividedProgressSeconds / e.multipliedSeconds;
    167 				multipliedProgressSeconds -= (float)(int)multipliedProgressSeconds;
    168 				multipliedProgressSeconds *= e.multipliedSeconds;
    169 				out += 2.0f * (float)(multipliedProgressSeconds <= e.gateSeconds);
    170 			}
    171 		}
    172 	}
    173 
    174 	out += _rangeOffset;
    175 	out *= _rangeScale;
    176 	outputs[GATE_OUTPUT].setChannels(_channels);
    177 	outputs[GATE_OUTPUT].setVoltage(out, c);
    178 }
    179 
    180 struct IPQuantity : Quantity {
    181 	RGate* _module;
    182 
    183 	IPQuantity(RGate* m) : _module(m) {}
    184 
    185 	void setValue(float value) override {
    186 		value = clamp(value, getMinValue(), getMaxValue());
    187 		if (_module) {
    188 			_module->_initialClockPeriod = value;
    189 		}
    190 	}
    191 
    192 	float getValue() override {
    193 		if (_module) {
    194 			return _module->_initialClockPeriod;
    195 		}
    196 		return RGate::defaultInitialClockPeriod;
    197 	}
    198 
    199 	float getMinValue() override { return 0.0f; }
    200 	float getMaxValue() override { return 1.0f; }
    201 	float getDefaultValue() override { return RGate::defaultInitialClockPeriod; }
    202 	float getDisplayValue() override { return getValue() * 1000.0f; }
    203 	void setDisplayValue(float displayValue) override { setValue(displayValue / 1000.0f); }
    204 	std::string getLabel() override { return "Initial clock"; }
    205 	std::string getUnit() override { return "ms"; }
    206 };
    207 
    208 struct IPSlider : ui::Slider {
    209 	IPSlider(RGate* module) {
    210 		quantity = new IPQuantity(module);
    211 		box.size.x = 200.0f;
    212 	}
    213 	virtual ~IPSlider() {
    214 		delete quantity;
    215 	}
    216 };
    217 
    218 struct IPMenuItem : MenuItem {
    219 	RGate* _module;
    220 
    221 	IPMenuItem(RGate* m) : _module(m) {
    222 		this->text = "Initial clock";
    223 		this->rightText = "▸";
    224 	}
    225 
    226 	Menu* createChildMenu() override {
    227 		Menu* menu = new Menu;
    228 		menu->addChild(new IPSlider(_module));
    229 		return menu;
    230 	}
    231 };
    232 
    233 struct RGateWidget : BGModuleWidget {
    234 	static constexpr int hp = 5;
    235 
    236 	RGateWidget(RGate* module) {
    237 		setModule(module);
    238 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    239 		setPanel(box.size, "RGate");
    240 		createScrews();
    241 
    242 		// generated by svg_widgets.rb
    243 		auto lengthParamPosition = Vec(18.5, 48.0);
    244 		auto clockDivideParamPosition = Vec(24.5, 129.0);
    245 		auto clockMultiplyParamPosition = Vec(24.5, 200.0);
    246 
    247 		auto lengthInputPosition = Vec(10.5, 251.0);
    248 		auto clockDivideInputPosition = Vec(40.5, 251.0);
    249 		auto resetInputPosition = Vec(10.5, 288.0);
    250 		auto clockMultipleInputPosition = Vec(40.5, 288.0);
    251 		auto clockInputPosition = Vec(10.5, 325.0);
    252 
    253 		auto gateOutputPosition = Vec(40.5, 325.0);
    254 		// end generated by svg_widgets.rb
    255 
    256 		addParam(createParam<Knob38>(lengthParamPosition, module, RGate::LENGTH_PARAM));
    257 		addParam(createParam<Knob26>(clockDivideParamPosition, module, RGate::CLOCK_DIVIDE_PARAM));
    258 		addParam(createParam<Knob26>(clockMultiplyParamPosition, module, RGate::CLOCK_MULTIPLY_PARAM));
    259 
    260 		addInput(createInput<Port24>(lengthInputPosition, module, RGate::LENGTH_INPUT));
    261 		addInput(createInput<Port24>(clockDivideInputPosition, module, RGate::CLOCK_DIVIDE_INPUT));
    262 		addInput(createInput<Port24>(resetInputPosition, module, RGate::RESET_INPUT));
    263 		addInput(createInput<Port24>(clockMultipleInputPosition, module, RGate::CLOCK_MULTIPLE_INPUT));
    264 		addInput(createInput<Port24>(clockInputPosition, module, RGate::CLOCK_INPUT));
    265 
    266 		addOutput(createOutput<Port24>(gateOutputPosition, module, RGate::GATE_OUTPUT));
    267 	}
    268 
    269 	void contextMenu(Menu* menu) override {
    270 		auto m = dynamic_cast<RGate*>(module);
    271 		assert(m);
    272 
    273 		OptionsMenuItem* p = new OptionsMenuItem("Polyphony channels from");
    274 		p->addItem(OptionMenuItem("CLOCK input", [m]() { return m->_polyInputID == RGate::CLOCK_INPUT; }, [m]() { m->_polyInputID = RGate::CLOCK_INPUT; }));
    275 		p->addItem(OptionMenuItem("LEN input", [m]() { return m->_polyInputID == RGate::LENGTH_INPUT; }, [m]() { m->_polyInputID = RGate::LENGTH_INPUT; }));
    276 		OptionsMenuItem::addToMenu(p, menu);
    277 
    278 		OptionsMenuItem* r = new OptionsMenuItem("RESET mode");
    279 		r->addItem(OptionMenuItem("Hard: reset clock period and divider", [m]() { return m->_resetMode == RGate::HARD_RESETMODE; }, [m]() {  m->_resetMode = RGate::HARD_RESETMODE; }));
    280 		r->addItem(OptionMenuItem("Soft: reseet clock divider", [m]() { return m->_resetMode == RGate::SOFT_RESETMODE; }, [m]() {  m->_resetMode = RGate::SOFT_RESETMODE; }));
    281 		OptionsMenuItem::addToMenu(r, menu);
    282 
    283 		menu->addChild(new IPMenuItem(m));
    284 
    285 		OptionsMenuItem* mi = new OptionsMenuItem("Range");
    286 		mi->addItem(OutputRangeOptionMenuItem(m, "0V-10V", 1.0f, 5.0f));
    287 		mi->addItem(OutputRangeOptionMenuItem(m, "0V-5V", 1.0f, 2.5f));
    288 		mi->addItem(OutputRangeOptionMenuItem(m, "+/-10V", 0.0f, 10.0f));
    289 		mi->addItem(OutputRangeOptionMenuItem(m, "+/-5V", 0.0f, 5.0f));
    290 		OptionsMenuItem::addToMenu(mi, menu);
    291 	}
    292 };
    293 
    294 Model* modelRGate = createModel<RGate, RGateWidget>("Bogaudio-RGate", "RGATE", "Clock-relative gate generator & clock divider/multiplier", "Clock modulator", "Polyphonic");