BogaudioModules

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

AD.cpp (5592B)


      1 
      2 #include "AD.hpp"
      3 
      4 #define INVERT "invert"
      5 
      6 void AD::Engine::reset() {
      7 	trigger.reset();
      8 	eocPulseGen.process(10.0);
      9 	envelope.reset();
     10 	on = false;
     11 }
     12 
     13 void AD::Engine::sampleRateChange() {
     14 	float sr = APP->engine->getSampleRate();
     15 	envelope.setSampleRate(sr);
     16 	attackSL.setParams(sr / (float)modulationSteps);
     17 	decaySL.setParams(sr / (float)modulationSteps);
     18 }
     19 
     20 void AD::reset() {
     21 	for (int c = 0; c < _channels; ++c) {
     22 		_engines[c]->reset();
     23 	}
     24 }
     25 
     26 void AD::sampleRateChange() {
     27 	for (int c = 0; c < _channels; ++c) {
     28 		_engines[c]->sampleRateChange();
     29 	}
     30 }
     31 
     32 json_t* AD::saveToJson(json_t* root) {
     33 	json_object_set_new(root, INVERT, json_real(_invert));
     34 	return root;
     35 }
     36 
     37 void AD::loadFromJson(json_t* root) {
     38 	json_t* i = json_object_get(root, INVERT);
     39 	if (i) {
     40 		_invert = json_real_value(i);
     41 	}
     42 }
     43 
     44 bool AD::active() {
     45 	return inputs[TRIGGER_INPUT].isConnected() || outputs[ENV_OUTPUT].isConnected() || outputs[EOC_OUTPUT].isConnected();
     46 }
     47 
     48 int AD::channels() {
     49 	return inputs[TRIGGER_INPUT].getChannels();
     50 }
     51 
     52 void AD::addChannel(int c) {
     53 	_engines[c] = new Engine(_modulationSteps);
     54 	_engines[c]->reset();
     55 	_engines[c]->sampleRateChange();
     56 }
     57 
     58 void AD::removeChannel(int c) {
     59 	delete _engines[c];
     60 	_engines[c] = NULL;
     61 }
     62 
     63 void AD::modulateChannel(int c) {
     64 	Engine& e = *_engines[c];
     65 
     66 	float attack = powf(params[ATTACK_PARAM].getValue(), 2.0f);
     67 	if (inputs[ATTACK_INPUT].isConnected()) {
     68 		attack *= clamp(inputs[ATTACK_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
     69 	}
     70 	e.envelope.setAttack(e.attackSL.next(attack * 10.f));
     71 
     72 	float decay = powf(params[DECAY_PARAM].getValue(), 2.0f);
     73 	if (inputs[DECAY_INPUT].isConnected()) {
     74 		decay *= clamp(inputs[DECAY_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
     75 	}
     76 	e.envelope.setDecay(e.decaySL.next(decay * 10.f));
     77 
     78 	e.envelope.setLinearShape(_linearMode);
     79 
     80 	_retriggerMode = params[RETRIGGER_PARAM].getValue() > 0.5f;
     81 	_loopMode = params[LOOP_PARAM].getValue() > 0.5f;
     82 	_linearMode = params[LINEAR_PARAM].getValue() > 0.5f;
     83 }
     84 
     85 void AD::processAlways(const ProcessArgs& args) {
     86 	_attackLightSum = _decayLightSum = 0;
     87 }
     88 
     89 void AD::processChannel(const ProcessArgs& args, int c) {
     90 	Engine& e = *_engines[c];
     91 
     92 	bool start = e.trigger.process(inputs[TRIGGER_INPUT].getVoltage(c));
     93 	if (!e.on && (
     94 		start ||
     95 		(_retriggerMode && e.trigger.isHigh()) ||
     96 		(_loopMode && e.envelope.isStage(ADSR::STOPPED_STAGE))
     97 	)) {
     98 		e.on = true;
     99 	} else if (e.on && start && _retriggerMode) {
    100 		if (_loopMode) {
    101 			e.envelope.reset();
    102 		}
    103 		else {
    104 			e.envelope.retrigger();
    105 		}
    106 	}
    107 	e.envelope.setGate(e.on);
    108 	outputs[ENV_OUTPUT].setChannels(_channels);
    109 	outputs[ENV_OUTPUT].setVoltage(e.envelope.next() * 10.0f * _invert, c);
    110 	if (e.on && e.envelope.isStage(ADSR::SUSTAIN_STAGE)) {
    111 		e.envelope.reset();
    112 		e.on = false;
    113 		e.eocPulseGen.trigger(0.001f);
    114 	}
    115 	outputs[EOC_OUTPUT].setChannels(_channels);
    116 	outputs[EOC_OUTPUT].setVoltage(e.eocPulseGen.process(APP->engine->getSampleTime()) ? 5.0f : 0.0f, c);
    117 
    118 	_attackLightSum += e.envelope.isStage(ADSR::ATTACK_STAGE);
    119 	_decayLightSum += e.envelope.isStage(ADSR::DECAY_STAGE);
    120 }
    121 
    122 void AD::postProcessAlways(const ProcessArgs& args) {
    123 	lights[ATTACK_LIGHT].value = _attackLightSum * _inverseChannels;
    124 	lights[DECAY_LIGHT].value = _decayLightSum * _inverseChannels;
    125 }
    126 
    127 struct ADWidget : BGModuleWidget {
    128 	static constexpr int hp = 3;
    129 
    130 	ADWidget(AD* module) {
    131 		setModule(module);
    132 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    133 		setPanel(box.size, "AD");
    134 		createScrews();
    135 
    136 		// generated by svg_widgets.rb
    137 		auto attackParamPosition = Vec(8.0, 33.0);
    138 		auto decayParamPosition = Vec(8.0, 90.0);
    139 		auto retriggerParamPosition = Vec(12.0, 131.5);
    140 		auto loopParamPosition = Vec(33.0, 131.5);
    141 		auto linearParamPosition = Vec(26.0, 146.0);
    142 
    143 		auto triggerInputPosition = Vec(10.5, 163.5);
    144 		auto attackInputPosition = Vec(10.5, 198.5);
    145 		auto decayInputPosition = Vec(10.5, 233.5);
    146 
    147 		auto envOutputPosition = Vec(10.5, 271.5);
    148 		auto eocOutputPosition = Vec(10.5, 306.5);
    149 
    150 		auto attackLightPosition = Vec(20.8, 65.0);
    151 		auto decayLightPosition = Vec(20.8, 122.0);
    152 		// end generated by svg_widgets.rb
    153 
    154 		addParam(createParam<Knob29>(attackParamPosition, module, AD::ATTACK_PARAM));
    155 		addParam(createParam<Knob29>(decayParamPosition, module, AD::DECAY_PARAM));
    156 		addParam(createParam<IndicatorButtonGreen9>(loopParamPosition, module, AD::LOOP_PARAM));
    157 		addParam(createParam<IndicatorButtonGreen9>(linearParamPosition, module, AD::LINEAR_PARAM));
    158 		addParam(createParam<IndicatorButtonGreen9>(retriggerParamPosition, module, AD::RETRIGGER_PARAM));
    159 
    160 		addInput(createInput<Port24>(triggerInputPosition, module, AD::TRIGGER_INPUT));
    161 		addInput(createInput<Port24>(attackInputPosition, module, AD::ATTACK_INPUT));
    162 		addInput(createInput<Port24>(decayInputPosition, module, AD::DECAY_INPUT));
    163 
    164 		addOutput(createOutput<Port24>(envOutputPosition, module, AD::ENV_OUTPUT));
    165 		addOutput(createOutput<Port24>(eocOutputPosition, module, AD::EOC_OUTPUT));
    166 
    167 		addChild(createLight<BGTinyLight<GreenLight>>(attackLightPosition, module, AD::ATTACK_LIGHT));
    168 		addChild(createLight<BGTinyLight<GreenLight>>(decayLightPosition, module, AD::DECAY_LIGHT));
    169 	}
    170 
    171 	void contextMenu(Menu* menu) override {
    172 		auto m = dynamic_cast<AD*>(module);
    173 		assert(m);
    174 		menu->addChild(new OptionMenuItem(
    175 			"Invert output",
    176 			[m]() { return m->_invert == -1.0f; },
    177 			[m]() {
    178 				if (m->_invert < 0.0f) {
    179 					m->_invert = 1.0f;
    180 				}
    181 				else {
    182 					m->_invert = -1.0f;
    183 				}
    184 			}
    185 		));
    186 	}
    187 };
    188 
    189 Model* modelAD = bogaudio::createModel<AD, ADWidget>("Bogaudio-AD", "AD", "Attack/decay envelope generator", "Envelope generator", "Polyphonic");