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.

154 lines
5.1KB

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