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.

141 lines
4.2KB

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