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.

140 lines
3.9KB

  1. #include "Fundamental.hpp"
  2. #include "dsp/digital.hpp"
  3. struct ADSR : Module {
  4. enum ParamIds {
  5. ATTACK_PARAM,
  6. DECAY_PARAM,
  7. SUSTAIN_PARAM,
  8. RELEASE_PARAM,
  9. NUM_PARAMS
  10. };
  11. enum InputIds {
  12. ATTACK_INPUT,
  13. DECAY_INPUT,
  14. SUSTAIN_INPUT,
  15. RELEASE_INPUT,
  16. GATE_INPUT,
  17. TRIG_INPUT,
  18. NUM_INPUTS
  19. };
  20. enum OutputIds {
  21. ENVELOPE_OUTPUT,
  22. NUM_OUTPUTS
  23. };
  24. enum LightIds {
  25. ATTACK_LIGHT,
  26. DECAY_LIGHT,
  27. SUSTAIN_LIGHT,
  28. RELEASE_LIGHT,
  29. NUM_LIGHTS
  30. };
  31. bool decaying = false;
  32. float env = 0.0;
  33. SchmittTrigger trigger;
  34. ADSR() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  35. trigger.setThresholds(0.0, 1.0);
  36. }
  37. void step() override;
  38. };
  39. void ADSR::step() {
  40. float attack = clampf(params[ATTACK_INPUT].value + inputs[ATTACK_INPUT].value / 10.0, 0.0, 1.0);
  41. float decay = clampf(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0, 0.0, 1.0);
  42. float sustain = clampf(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0, 0.0, 1.0);
  43. float release = clampf(params[RELEASE_PARAM].value + inputs[RELEASE_PARAM].value / 10.0, 0.0, 1.0);
  44. // Gate and trigger
  45. bool gated = inputs[GATE_INPUT].value >= 1.0;
  46. if (trigger.process(inputs[TRIG_INPUT].value))
  47. decaying = false;
  48. const float base = 20000.0;
  49. const float maxTime = 10.0;
  50. if (gated) {
  51. if (decaying) {
  52. // Decay
  53. if (decay < 1e-4) {
  54. env = sustain;
  55. }
  56. else {
  57. env += powf(base, 1 - decay) / maxTime * (sustain - env) / engineGetSampleRate();
  58. }
  59. }
  60. else {
  61. // Attack
  62. // Skip ahead if attack is all the way down (infinitely fast)
  63. if (attack < 1e-4) {
  64. env = 1.0;
  65. }
  66. else {
  67. env += powf(base, 1 - attack) / maxTime * (1.01 - env) / engineGetSampleRate();
  68. }
  69. if (env >= 1.0) {
  70. env = 1.0;
  71. decaying = true;
  72. }
  73. }
  74. }
  75. else {
  76. // Release
  77. if (release < 1e-4) {
  78. env = 0.0;
  79. }
  80. else {
  81. env += powf(base, 1 - release) / maxTime * (0.0 - env) / engineGetSampleRate();
  82. }
  83. decaying = false;
  84. }
  85. outputs[ENVELOPE_OUTPUT].value = 10.0 * env;
  86. // Lights
  87. lights[ATTACK_LIGHT].value = (gated && !decaying) ? 1.0 : 0.0;
  88. lights[DECAY_LIGHT].value = (gated && decaying) ? 1.0 : 0.0;
  89. lights[SUSTAIN_LIGHT].setBrightness(env);
  90. lights[RELEASE_LIGHT].value = (!gated) ? 1.0 : 0.0;
  91. }
  92. ADSRWidget::ADSRWidget() {
  93. ADSR *module = new ADSR();
  94. setModule(module);
  95. box.size = Vec(15*8, 380);
  96. {
  97. SVGPanel *panel = new SVGPanel();
  98. panel->box.size = box.size;
  99. panel->setBackground(SVG::load(assetPlugin(plugin, "res/ADSR.svg")));
  100. addChild(panel);
  101. }
  102. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  103. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
  104. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  105. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
  106. addParam(createParam<RoundBlackKnob>(Vec(62, 57), module, ADSR::ATTACK_PARAM, 0.0, 1.0, 0.5));
  107. addParam(createParam<RoundBlackKnob>(Vec(62, 124), module, ADSR::DECAY_PARAM, 0.0, 1.0, 0.5));
  108. addParam(createParam<RoundBlackKnob>(Vec(62, 191), module, ADSR::SUSTAIN_PARAM, 0.0, 1.0, 0.5));
  109. addParam(createParam<RoundBlackKnob>(Vec(62, 257), module, ADSR::RELEASE_PARAM, 0.0, 1.0, 0.5));
  110. addInput(createInput<PJ301MPort>(Vec(9, 63), module, ADSR::ATTACK_INPUT));
  111. addInput(createInput<PJ301MPort>(Vec(9, 129), module, ADSR::DECAY_INPUT));
  112. addInput(createInput<PJ301MPort>(Vec(9, 196), module, ADSR::SUSTAIN_INPUT));
  113. addInput(createInput<PJ301MPort>(Vec(9, 263), module, ADSR::RELEASE_INPUT));
  114. addInput(createInput<PJ301MPort>(Vec(9, 320), module, ADSR::GATE_INPUT));
  115. addInput(createInput<PJ301MPort>(Vec(48, 320), module, ADSR::TRIG_INPUT));
  116. addOutput(createOutput<PJ301MPort>(Vec(87, 320), module, ADSR::ENVELOPE_OUTPUT));
  117. addChild(createLight<SmallLight<RedLight>>(Vec(94, 41), module, ADSR::ATTACK_LIGHT));
  118. addChild(createLight<SmallLight<RedLight>>(Vec(94, 109), module, ADSR::DECAY_LIGHT));
  119. addChild(createLight<SmallLight<RedLight>>(Vec(94, 175), module, ADSR::SUSTAIN_LIGHT));
  120. addChild(createLight<SmallLight<RedLight>>(Vec(94, 242), module, ADSR::RELEASE_LIGHT));
  121. }