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.

139 lines
4.3KB

  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.0f;
  33. SchmittTrigger trigger;
  34. ADSR() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  35. void step() override;
  36. };
  37. void ADSR::step() {
  38. float attack = clamp(params[ATTACK_INPUT].value + inputs[ATTACK_INPUT].value / 10.0f, 0.0f, 1.0f);
  39. float decay = clamp(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0f, 0.0f, 1.0f);
  40. float sustain = clamp(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f);
  41. float release = clamp(params[RELEASE_PARAM].value + inputs[RELEASE_PARAM].value / 10.0f, 0.0f, 1.0f);
  42. // Gate and trigger
  43. bool gated = inputs[GATE_INPUT].value >= 1.0f;
  44. if (trigger.process(inputs[TRIG_INPUT].value))
  45. decaying = false;
  46. const float base = 20000.0f;
  47. const float maxTime = 10.0f;
  48. if (gated) {
  49. if (decaying) {
  50. // Decay
  51. if (decay < 1e-4) {
  52. env = sustain;
  53. }
  54. else {
  55. env += powf(base, 1 - decay) / maxTime * (sustain - env) * engineGetSampleTime();
  56. }
  57. }
  58. else {
  59. // Attack
  60. // Skip ahead if attack is all the way down (infinitely fast)
  61. if (attack < 1e-4) {
  62. env = 1.0f;
  63. }
  64. else {
  65. env += powf(base, 1 - attack) / maxTime * (1.01f - env) * engineGetSampleTime();
  66. }
  67. if (env >= 1.0f) {
  68. env = 1.0f;
  69. decaying = true;
  70. }
  71. }
  72. }
  73. else {
  74. // Release
  75. if (release < 1e-4) {
  76. env = 0.0f;
  77. }
  78. else {
  79. env += powf(base, 1 - release) / maxTime * (0.0f - env) * engineGetSampleTime();
  80. }
  81. decaying = false;
  82. }
  83. bool sustaining = isNear(env, sustain, 1e-3);
  84. bool resting = isNear(env, 0.0f, 1e-3);
  85. outputs[ENVELOPE_OUTPUT].value = 10.0f * env;
  86. // Lights
  87. lights[ATTACK_LIGHT].value = (gated && !decaying) ? 1.0f : 0.0f;
  88. lights[DECAY_LIGHT].value = (gated && decaying && !sustaining) ? 1.0f : 0.0f;
  89. lights[SUSTAIN_LIGHT].value = (gated && decaying && sustaining) ? 1.0f : 0.0f;
  90. lights[RELEASE_LIGHT].value = (!gated && !resting) ? 1.0f : 0.0f;
  91. }
  92. struct ADSRWidget : ModuleWidget {
  93. ADSRWidget(ADSR *module);
  94. };
  95. ADSRWidget::ADSRWidget(ADSR *module) : ModuleWidget(module) {
  96. setPanel(SVG::load(assetPlugin(plugin, "res/ADSR.svg")));
  97. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  98. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  99. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  100. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  101. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(62, 57), module, ADSR::ATTACK_PARAM, 0.0f, 1.0f, 0.5f));
  102. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(62, 124), module, ADSR::DECAY_PARAM, 0.0f, 1.0f, 0.5f));
  103. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(62, 191), module, ADSR::SUSTAIN_PARAM, 0.0f, 1.0f, 0.5f));
  104. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(62, 257), module, ADSR::RELEASE_PARAM, 0.0f, 1.0f, 0.5f));
  105. addInput(Port::create<PJ301MPort>(Vec(9, 63), Port::INPUT, module, ADSR::ATTACK_INPUT));
  106. addInput(Port::create<PJ301MPort>(Vec(9, 129), Port::INPUT, module, ADSR::DECAY_INPUT));
  107. addInput(Port::create<PJ301MPort>(Vec(9, 196), Port::INPUT, module, ADSR::SUSTAIN_INPUT));
  108. addInput(Port::create<PJ301MPort>(Vec(9, 263), Port::INPUT, module, ADSR::RELEASE_INPUT));
  109. addInput(Port::create<PJ301MPort>(Vec(9, 320), Port::INPUT, module, ADSR::GATE_INPUT));
  110. addInput(Port::create<PJ301MPort>(Vec(48, 320), Port::INPUT, module, ADSR::TRIG_INPUT));
  111. addOutput(Port::create<PJ301MPort>(Vec(87, 320), Port::OUTPUT, module, ADSR::ENVELOPE_OUTPUT));
  112. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(94, 41), module, ADSR::ATTACK_LIGHT));
  113. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(94, 109), module, ADSR::DECAY_LIGHT));
  114. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(94, 175), module, ADSR::SUSTAIN_LIGHT));
  115. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(94, 242), module, ADSR::RELEASE_LIGHT));
  116. }
  117. Model *modelADSR = Model::create<ADSR, ADSRWidget>("Fundamental", "ADSR", "ADSR", ENVELOPE_GENERATOR_TAG);