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