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.

184 lines
6.4KB

  1. #include "plugin.hpp"
  2. using namespace simd;
  3. const float MIN_TIME = 1e-3f;
  4. const float MAX_TIME = 10.f;
  5. const float LAMBDA_BASE = MAX_TIME / MIN_TIME;
  6. struct ADSR : Module {
  7. enum ParamIds {
  8. ATTACK_PARAM,
  9. DECAY_PARAM,
  10. SUSTAIN_PARAM,
  11. RELEASE_PARAM,
  12. NUM_PARAMS
  13. };
  14. enum InputIds {
  15. ATTACK_INPUT,
  16. DECAY_INPUT,
  17. SUSTAIN_INPUT,
  18. RELEASE_INPUT,
  19. GATE_INPUT,
  20. TRIG_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. ENVELOPE_OUTPUT,
  25. NUM_OUTPUTS
  26. };
  27. enum LightIds {
  28. ATTACK_LIGHT,
  29. DECAY_LIGHT,
  30. SUSTAIN_LIGHT,
  31. RELEASE_LIGHT,
  32. NUM_LIGHTS
  33. };
  34. float_4 attacking[4] = {float_4::zero()};
  35. float_4 env[4] = {0.f};
  36. dsp::TSchmittTrigger<float_4> trigger[4];
  37. dsp::ClockDivider cvDivider;
  38. float_4 attackLambda[4] = {0.f};
  39. float_4 decayLambda[4] = {0.f};
  40. float_4 releaseLambda[4] = {0.f};
  41. float_4 sustain[4] = {0.f};
  42. dsp::ClockDivider lightDivider;
  43. ADSR() {
  44. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  45. configParam(ATTACK_PARAM, 0.f, 1.f, 0.5f, "Attack", " ms", LAMBDA_BASE, MIN_TIME * 1000);
  46. configParam(DECAY_PARAM, 0.f, 1.f, 0.5f, "Decay", " ms", LAMBDA_BASE, MIN_TIME * 1000);
  47. configParam(SUSTAIN_PARAM, 0.f, 1.f, 0.5f, "Sustain", "%", 0, 100);
  48. configParam(RELEASE_PARAM, 0.f, 1.f, 0.5f, "Release", " ms", LAMBDA_BASE, MIN_TIME * 1000);
  49. cvDivider.setDivision(16);
  50. lightDivider.setDivision(128);
  51. }
  52. void process(const ProcessArgs &args) override {
  53. // 0.16-0.19 us serial
  54. // 0.23 us serial with all lambdas computed
  55. // 0.15-0.18 us serial with all lambdas computed with SSE
  56. int channels = inputs[GATE_INPUT].getChannels();
  57. // Compute lambdas
  58. if (cvDivider.process()) {
  59. float attackParam = params[ATTACK_PARAM].getValue();
  60. float decayParam = params[DECAY_PARAM].getValue();
  61. float sustainParam = params[SUSTAIN_PARAM].getValue();
  62. float releaseParam = params[RELEASE_PARAM].getValue();
  63. for (int c = 0; c < channels; c += 4) {
  64. // CV
  65. float_4 attack = attackParam + inputs[ATTACK_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f;
  66. float_4 decay = decayParam + inputs[DECAY_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f;
  67. float_4 sustain = sustainParam + inputs[SUSTAIN_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f;
  68. float_4 release = releaseParam + inputs[RELEASE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f;
  69. attack = simd::clamp(attack, 0.f, 1.f);
  70. decay = simd::clamp(decay, 0.f, 1.f);
  71. sustain = simd::clamp(sustain, 0.f, 1.f);
  72. release = simd::clamp(release, 0.f, 1.f);
  73. attackLambda[c / 4] = simd::pow(LAMBDA_BASE, -attack) / MIN_TIME;
  74. decayLambda[c / 4] = simd::pow(LAMBDA_BASE, -decay) / MIN_TIME;
  75. releaseLambda[c / 4] = simd::pow(LAMBDA_BASE, -release) / MIN_TIME;
  76. this->sustain[c / 4] = sustain;
  77. }
  78. }
  79. float_4 gate[4];
  80. for (int c = 0; c < channels; c += 4) {
  81. // Gate
  82. gate[c / 4] = inputs[GATE_INPUT].getVoltageSimd<float_4>(c) >= 1.f;
  83. // Retrigger
  84. float_4 triggered = trigger[c / 4].process(inputs[TRIG_INPUT].getPolyVoltageSimd<float_4>(c));
  85. attacking[c / 4] = simd::ifelse(triggered, float_4::mask(), attacking[c / 4]);
  86. // Get target and lambda for exponential decay
  87. const float attackTarget = 1.2f;
  88. float_4 target = simd::ifelse(gate[c / 4], simd::ifelse(attacking[c / 4], attackTarget, sustain[c / 4]), 0.f);
  89. float_4 lambda = simd::ifelse(gate[c / 4], simd::ifelse(attacking[c / 4], attackLambda[c / 4], decayLambda[c / 4]), releaseLambda[c / 4]);
  90. // Adjust env
  91. env[c / 4] += (target - env[c / 4]) * lambda * args.sampleTime;
  92. // Turn off attacking state if envelope is HIGH
  93. attacking[c / 4] = simd::ifelse(env[c / 4] >= 1.f, float_4::zero(), attacking[c / 4]);
  94. // Turn on attacking state if gate is LOW
  95. attacking[c / 4] = simd::ifelse(gate[c / 4], attacking[c / 4], float_4::mask());
  96. // Set output
  97. outputs[ENVELOPE_OUTPUT].setVoltageSimd(10.f * env[c / 4], c);
  98. }
  99. outputs[ENVELOPE_OUTPUT].setChannels(channels);
  100. // Lights
  101. if (lightDivider.process()) {
  102. lights[ATTACK_LIGHT].setBrightness(0);
  103. lights[DECAY_LIGHT].setBrightness(0);
  104. lights[SUSTAIN_LIGHT].setBrightness(0);
  105. lights[RELEASE_LIGHT].setBrightness(0);
  106. for (int c = 0; c < channels; c += 4) {
  107. const float epsilon = 0.01f;
  108. float_4 sustaining = (sustain[c / 4] <= env[c / 4]) & (env[c / 4] < sustain[c / 4] + epsilon);
  109. float_4 resting = (env[c / 4] < epsilon);
  110. if (simd::movemask(gate[c / 4] & attacking[c / 4]))
  111. lights[ATTACK_LIGHT].setBrightness(1);
  112. if (simd::movemask(gate[c / 4] & ~attacking[c / 4] & ~sustaining))
  113. lights[DECAY_LIGHT].setBrightness(1);
  114. if (simd::movemask(gate[c / 4] & ~attacking[c / 4] & sustaining))
  115. lights[SUSTAIN_LIGHT].setBrightness(1);
  116. if (simd::movemask(~gate[c / 4] & ~resting))
  117. lights[RELEASE_LIGHT].setBrightness(1);
  118. }
  119. }
  120. }
  121. };
  122. struct ADSRWidget : ModuleWidget {
  123. ADSRWidget(ADSR *module) {
  124. setModule(module);
  125. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ADSR.svg")));
  126. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  127. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  128. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  129. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  130. addParam(createParam<RoundLargeBlackKnob>(Vec(62, 57), module, ADSR::ATTACK_PARAM));
  131. addParam(createParam<RoundLargeBlackKnob>(Vec(62, 124), module, ADSR::DECAY_PARAM));
  132. addParam(createParam<RoundLargeBlackKnob>(Vec(62, 191), module, ADSR::SUSTAIN_PARAM));
  133. addParam(createParam<RoundLargeBlackKnob>(Vec(62, 257), module, ADSR::RELEASE_PARAM));
  134. addInput(createInput<PJ301MPort>(Vec(9, 63), module, ADSR::ATTACK_INPUT));
  135. addInput(createInput<PJ301MPort>(Vec(9, 129), module, ADSR::DECAY_INPUT));
  136. addInput(createInput<PJ301MPort>(Vec(9, 196), module, ADSR::SUSTAIN_INPUT));
  137. addInput(createInput<PJ301MPort>(Vec(9, 263), module, ADSR::RELEASE_INPUT));
  138. addInput(createInput<PJ301MPort>(Vec(9, 320), module, ADSR::GATE_INPUT));
  139. addInput(createInput<PJ301MPort>(Vec(48, 320), module, ADSR::TRIG_INPUT));
  140. addOutput(createOutput<PJ301MPort>(Vec(87, 320), module, ADSR::ENVELOPE_OUTPUT));
  141. addChild(createLight<SmallLight<RedLight>>(Vec(94, 41), module, ADSR::ATTACK_LIGHT));
  142. addChild(createLight<SmallLight<RedLight>>(Vec(94, 109), module, ADSR::DECAY_LIGHT));
  143. addChild(createLight<SmallLight<RedLight>>(Vec(94, 175), module, ADSR::SUSTAIN_LIGHT));
  144. addChild(createLight<SmallLight<RedLight>>(Vec(94, 242), module, ADSR::RELEASE_LIGHT));
  145. }
  146. };
  147. Model *modelADSR = createModel<ADSR, ADSRWidget>("ADSR");