You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
5.2KB

  1. //**************************************************************************************
  2. //ADSR module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS
  3. //
  4. //Code taken from the Fundamentals plugins by Andrew Belt http://www.vcvrack.com
  5. //**************************************************************************************
  6. #include "AS.hpp"
  7. #include "dsp/digital.hpp"
  8. namespace rack_plugin_AS {
  9. struct ADSR : Module {
  10. enum ParamIds {
  11. ATTACK_PARAM,
  12. DECAY_PARAM,
  13. SUSTAIN_PARAM,
  14. RELEASE_PARAM,
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. ATTACK_INPUT,
  19. DECAY_INPUT,
  20. SUSTAIN_INPUT,
  21. RELEASE_INPUT,
  22. GATE_INPUT,
  23. TRIG_INPUT,
  24. NUM_INPUTS
  25. };
  26. enum OutputIds {
  27. ENVELOPE_OUTPUT,
  28. NUM_OUTPUTS
  29. };
  30. enum LightIds {
  31. ATTACK_LIGHT,
  32. DECAY_LIGHT,
  33. SUSTAIN_LIGHT,
  34. RELEASE_LIGHT,
  35. NUM_LIGHTS
  36. };
  37. bool decaying = false;
  38. float env = 0.0f;
  39. SchmittTrigger trigger;
  40. ADSR() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  41. }
  42. void step() override;
  43. };
  44. void ADSR::step() {
  45. float attack = clamp(params[ATTACK_PARAM].value + inputs[ATTACK_INPUT].value / 10.0f, 0.0f, 1.0f);
  46. float decay = clamp(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0f, 0.0f, 1.0f);
  47. float sustain = clamp(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f);
  48. float release = clamp(params[RELEASE_PARAM].value + inputs[RELEASE_INPUT].value / 10.0f, 0.0f, 1.0f);
  49. // Gate and trigger
  50. bool gated = inputs[GATE_INPUT].value >= 1.0f;
  51. if (trigger.process(inputs[TRIG_INPUT].value))
  52. decaying = false;
  53. const float base = 20000.0f;
  54. const float maxTime = 10.0f;
  55. if (gated) {
  56. if (decaying) {
  57. // Decay
  58. if (decay < 1e-4) {
  59. env = sustain;
  60. }
  61. else {
  62. env += powf(base, 1 - decay) / maxTime * (sustain - env) / engineGetSampleRate();
  63. }
  64. }
  65. else {
  66. // Attack
  67. // Skip ahead if attack is all the way down (infinitely fast)
  68. if (attack < 1e-4) {
  69. env = 1.0f;
  70. }
  71. else {
  72. env += powf(base, 1 - attack) / maxTime * (1.01 - env) / engineGetSampleRate();
  73. }
  74. if (env >= 1.0f) {
  75. env = 1.0f;
  76. decaying = true;
  77. }
  78. }
  79. }
  80. else {
  81. // Release
  82. if (release < 1e-4) {
  83. env = 0.0f;
  84. }
  85. else {
  86. env += powf(base, 1 - release) / maxTime * (0.0 - env) / engineGetSampleRate();
  87. }
  88. decaying = false;
  89. }
  90. bool sustaining = isNear(env, sustain, 1e-3);
  91. bool resting = isNear(env, 0.0, 1e-3);
  92. outputs[ENVELOPE_OUTPUT].value = 10.0f * env;
  93. // Lights
  94. lights[ATTACK_LIGHT].value = (gated && !decaying) ? 1.0f : 0.0f;
  95. lights[DECAY_LIGHT].value = (gated && decaying && !sustaining) ? 1.0f : 0.0f;
  96. lights[SUSTAIN_LIGHT].value = (gated && decaying && sustaining) ? 1.0f : 0.0f;
  97. lights[RELEASE_LIGHT].value = (!gated && !resting) ? 1.0f : 0.0f;
  98. }
  99. struct ADSRWidget : ModuleWidget
  100. {
  101. ADSRWidget(ADSR *module);
  102. };
  103. ADSRWidget::ADSRWidget(ADSR *module) : ModuleWidget(module) {
  104. box.size = Vec(RACK_GRID_WIDTH*8, RACK_GRID_HEIGHT);
  105. {
  106. SVGPanel *panel = new SVGPanel();
  107. panel->box.size = box.size;
  108. panel->setBackground(SVG::load(assetPlugin(plugin, "res/ADSR.svg")));
  109. addChild(panel);
  110. }
  111. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  112. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  113. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  114. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  115. static const float posX[4] = {13.0f,39.0f,65.0f,91.0f};
  116. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(posX[0]+6, 74), module, ADSR::ATTACK_LIGHT));
  117. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(posX[1]+6, 74), module, ADSR::DECAY_LIGHT));
  118. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(posX[2]+6, 74), module, ADSR::SUSTAIN_LIGHT));
  119. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(posX[3]+6, 74), module, ADSR::RELEASE_LIGHT));
  120. addParam(ParamWidget::create<as_SlidePot>(Vec(posX[0]-3, 90), module, ADSR::ATTACK_PARAM, 0.0f, 1.0f, 0.5f));
  121. addParam(ParamWidget::create<as_SlidePot>(Vec(posX[1]-3, 90), module, ADSR::DECAY_PARAM, 0.0f, 1.0f, 0.5f));
  122. addParam(ParamWidget::create<as_SlidePot>(Vec(posX[2]-3, 90), module, ADSR::SUSTAIN_PARAM, 0.0f, 1.0f, 0.5f));
  123. addParam(ParamWidget::create<as_SlidePot>(Vec(posX[3]-3, 90), module, ADSR::RELEASE_PARAM, 0.0f, 1.0f, 0.5f));
  124. addInput(Port::create<as_PJ301MPort>(Vec(posX[0]-4, 217), Port::INPUT, module, ADSR::ATTACK_INPUT));
  125. addInput(Port::create<as_PJ301MPort>(Vec(posX[1]-4, 217), Port::INPUT, module, ADSR::DECAY_INPUT));
  126. addInput(Port::create<as_PJ301MPort>(Vec(posX[2]-4, 217), Port::INPUT, module, ADSR::SUSTAIN_INPUT));
  127. addInput(Port::create<as_PJ301MPort>(Vec(posX[3]-4, 217), Port::INPUT, module, ADSR::RELEASE_INPUT));
  128. addInput(Port::create<as_PJ301MPort>(Vec(posX[0]-4, 310), Port::INPUT, module, ADSR::GATE_INPUT));
  129. addInput(Port::create<as_PJ301MPort>(Vec(48, 310), Port::INPUT, module, ADSR::TRIG_INPUT));
  130. addOutput(Port::create<as_PJ301MPort>(Vec(posX[3]-4, 310), Port::OUTPUT, module, ADSR::ENVELOPE_OUTPUT));
  131. }
  132. } // namespace rack_plugin_AS
  133. using namespace rack_plugin_AS;
  134. RACK_PLUGIN_MODEL_INIT(AS, ADSR) {
  135. Model *modelADSR = Model::create<ADSR, ADSRWidget>("AS", "ADSR", "ADSR", ENVELOPE_GENERATOR_TAG);
  136. return modelADSR;
  137. }